-
Arduino code
08/18/2014 at 14:48 • 0 comments/* Divergence: A wearable EMF detector with haptic and sonic feedback
* By Afroditi Psarra http://afroditipsarra.com
*
* July 2014
*
* Circuit:
* Adafruit Flora
* 2 handmade copper coils connected on analog pins A7 and A9 and grounded with 3.6 M Ohms resistors
* 1 vibration motor on digital pin 10 (PWM pin)
* 1 mini jack - soundOut connected to digital pin 12 via a low-pass filter with a 47uF
* and a zipper slider < 100 K Ohms (the zipper uses the uC internal pullup resistor
*/
#define soundOut 12 // sound output connected to digital pin 12
#define vibe 10 // vibration motor located on the chest connected to digital pin 3
#define coilRight A7 // coil antenna located on the right wrist connected to analog pin A7
#define coilLeft A9 // coil antenna located on the left wrist connected to analog pin A9
#define NUMREADINGSRIGHT 15 // raise this number to increase data smoothing
#define NUMREADINGSLEFT 15 // raise this number to increase data smoothing
int senseLimitRight = 125; // raise this number to decrease sensitivity on the right probe(up to 1023 max)
int senseLimitLeft = 125; // raise this number to decrease sensitivity on the left probe(up to 1023 max)
int probeRight; // probe readings from the right coil
int probeLeft; // probe readings from the left coil
// variables for smoothing
int readingsRight[NUMREADINGSRIGHT]; // the readings from the analog input A7
int indexRight = 0; // the index of the current reading from the right coil
int totalRight = 0; // the running total pf the right coil
int averageRight = 0; // final average of the probe reading from the right coil
int readingsLeft[NUMREADINGSLEFT]; // the readings from the analog input A9
int indexLeft = 0; // the index of the current reading from the left coil
int totalLeft = 0; // the running total of the left coil
int averageLeft = 0; // final average of the probe reading from the left coil
void setup() {
//Serial.begin(9600); // initialize the serial communication (for debugging-calibrating only)
pinMode(soundOut, OUTPUT); // initialize the mini jack/speaker as an output
pinMode(A11, INPUT_PULLUP); // use the internal pullup resistor on the same pin as above
pinMode(vibe, OUTPUT); // initialize the mini jack/speaker as an output
}
void loop() {
coilProbeRight();
coilProbeLeft();
// Serial.print(averageRight);
// Serial.print(" ");
// Serial.println(averageLeft);
delay(1);
}
void coilProbeRight() {
probeRight = analogRead(coilRight);
if(probeRight >= 1){ // if the reading isn't zero, proceed
probeRight = constrain(probeRight, 1, senseLimitRight); // turn any reading higher than the senseLimit value into the senseLimit value
probeRight = map(probeRight, 1, senseLimitRight, 1, 1023); // remap the constrained value within a 1 to 1023 range
totalRight -= readingsRight[indexRight]; // subtract the last reading
readingsRight[indexRight] = probeRight; // read from the sensor
totalRight += readingsRight[indexRight]; // add the reading to the total
indexRight = (indexRight + 1); // advance to the next index
if (indexRight >= NUMREADINGSRIGHT) // if we're at the end of the array...
indexRight = 0; // ...wrap around to the beginning
averageRight = totalRight / NUMREADINGSRIGHT; // calculate the average
}
int vibeIntensity = map(averageRight, 32, 768, 0, 255); // map the average to pwm values
analogWrite(vibe, vibeIntensity); // make the motor vibrate depending on the average
int freq = map(averageRight, 32, 768, 880, 60);
int dur = map(freq, 880, 60, 10, 1000);
tone(soundOut, freq, dur);
}
void coilProbeLeft() {
probeLeft = analogRead(coilLeft);
if(probeLeft >= 1){ // if the reading isn't zero, proceed
probeLeft = constrain(probeLeft, 1, senseLimitLeft); // turn any reading higher than the senseLimit value into the senseLimit value
probeLeft = map(probeLeft, 1, senseLimitLeft, 1, 1023); // remap the constrained value within a 1 to 1023 range
totalLeft -= readingsLeft[indexLeft]; // subtract the last reading
readingsLeft[indexLeft] = probeLeft; // read from the sensor
totalLeft += readingsLeft[indexLeft]; // add the reading to the total
indexLeft = (indexLeft + 1); // advance to the next index
if (indexLeft >= NUMREADINGSLEFT) // if we're at the end of the array...
indexLeft = 0; // ...wrap around to the beginning
averageLeft = totalLeft / NUMREADINGSLEFT; // calculate the average
}
int vibeIntensity = map(averageLeft, 0, 1023, 0, 255); // map the average to pwm values
analogWrite(vibe, vibeIntensity); // make the motor vibrate depending on the average
int freq = map(averageLeft, 0, 1023, 880, 60);
int dur = map(freq, 880, 60, 10, 1000);
tone(soundOut, freq, dur);
}
-
Circuit Schematic
08/18/2014 at 11:47 • 0 commentsAlthough my initial idea was to use two vibration motors - each one of them corresponding to the left and right probe, after doing some testing, I soon realized that the motors were drawing too much current from the circuit and they were creating background noise to the sonic output. So, I decided to bypass the right motor (as the circuit was already sewn, there was no room for adding extra components that could regulate it).
This is the final circuit schematic:
-
Low-pass filter with zipper slider
07/12/2014 at 07:58 • 0 commentsMy next step was to create a zipper slider that would be used as a low-pass filter between the microcontroller and the mini-jack to provide the user with some kind of volume control for the headphones (and also filter any possible noise). After experimenting with different materials and techniques I decided to go with Kobakant's "zipper slider", but instead of using resistive thread 66 Yarn 22+3ply 110 PET (4 K Ohms/20 cm resistance) and Lame Life Saver conductive thread, I have used Plug and Wear's resistive tape (3.2 K Ohms per linear meter/yard) form one side of the zipper and Spark Fun'sConductive Thread 60g (Stainless Steel) (28 Ohms/30 cm resistance) on the other side. The side with the resistive tape is hooked up in the microcontroller and programmed to play a square wave 800 Hz tone, using the internal pull up resistor of the board, while the side with the conductive thread passes through a 47uF capacitor and ends at the speaker. Unzipping produces a higher pitch and when the zipper is completely open you get the highest pitch (in this case 880 Hz).
This is the code that I used for testing:
// Low-pass filter that uses a zipper slider instead of a potentiometer
int speaker = 12; // speaker connected to digital pin 12 (on Flora, D12 and A11 are the same pin)
void setup() {
pinMode(speaker, OUTPUT); // initialize speaker pin as an output pinMode(A11, INPUT_PULLUP); // use the internal pullup resistor
}
void loop() {
tone(speaker, 880); // play a square wave of 880 Hz
}
-
Testing the coils
07/12/2014 at 07:52 • 0 commentsFor testing the coils I based my code on Aaron Alai's EMF detector project and Collin Cunningham's EMF experiments. I tested various resistors for their sensitivity and also calculated the resistance of the conductive thread that would be used for the soft circuit and decided to use a 3.6 M Ohms resistor on the ground connection of the coil and connected the other end to an analog pin. I tested the values that I got by printing them on the Serial Monitor and then added a speaker to have a sonic feedback of what was going on. I used a "smoothing" technique of creating an array an calculating the average value (as the previous authors suggested) in order to filter any unwanted noise to the results and then I mapped the values to a range of frequencies between 20 and 880 Hz and their respected durations between 10 to 500 milliseconds. The frequency mapping as can be observed in the code is inverted so that the higher the signal the higher the pitch and the lower the signal the lower the pitch. I got quite some interesting results as when I moved the coil closer to my mobile phone and the laptop I was getting lower frequencies and when I moved it closer to the plug where my laptop's transformer was I got higher and higher frequencies.
This is the code that I used:
// DivergenceCoil
// code on Flora
#define NUMREADINGS 15 // raise this number to increase data smoothing
int senseLimit = 15; // raise this number to decrease sensitivity (up to 1023 max)
int probePin = A7; // analog 7
int val = 0; // reading from probePin
int soundOut = 12;
// variables for smoothing
int readings[NUMREADINGS]; // the readings from the analog input
int index = 0; // the index of the current reading
int total = 0; // the running total
int average = 0; // final average of the probe reading
void setup() {
pinMode(soundOut, OUTPUT);
Serial.begin(9600); // initiate serial connection for debugging/etc
for (int i = 0; i < NUMREADINGS; i++)
readings[i] = 0; // initialize all the readings to 0
}
void loop() {
val = analogRead(probePin); // take a reading from the probe
if(val >= 1){ // if the reading isn't zero, proceed
val = constrain(val, 1, senseLimit); // turn any reading higher than the senseLimit value into the senseLimit value
val = map(val, 1, senseLimit, 1, 1023); // remap the constrained value within a 1 to 1023 range
total -= readings[index]; // subtract the last reading
readings[index] = val; // read from the sensor
total += readings[index]; // add the reading to the total
index = (index + 1); // advance to the next index
if (index >= NUMREADINGS) // if we're at the end of the array...
index = 0; // ...wrap around to the beginning
average = total / NUMREADINGS; // calculate the average
int freq = map(average, 0, 1023, 880, 20);
int dur = map(freq, 880, 20, 10, 500);
tone(soundOut, freq, dur);
Serial.print(val); // use output to aid in calibrating
Serial.print(" ");
Serial.print(average);
Serial.print(" ");
Serial.println(freq);
Serial.print(" ");
Serial.println(dur);
delay(1);
}
}