Today I took my first stab at coding some general magnetic detection code that will work with a magnetometer to detect finger presence, finder flexion, and finger extension, all based on magnet proximity to make a magnet HID. I haven’t even received my magnetometer or Leonardo yet (the Uno doesn’t support HID emulation), so I’m sure this code won’t work in practice, but it compiles and I’ll use it as a v. 0.01 for future enhancements. My only two concerns are finding a sweet spot for the finger to rest (perhaps foam above the magnetometer?), and saturation of the Hall effect chip the magnetometer runs on, but I’ll jump off that cliff when I come to it.
#include <Wire.h>; //Magnetometer I2C address (Default on mine) #define magaddress 0x1E //Define the number of samples you want to take, separated by the given delay in ms, to be averaged to check finger position #define pollcount 25 #define polldelay 10 //Define which axis is the one we care about (0=x, 1=y, 2=z). Note that proximity to the sensor should be an increase in the number, not a decrease. #define mainaxis 2 //Turn on or off reporting of axis value for each sample #define debug 1 int baseline, fingerin, fingerup, fingerdown, midline, downthreshold, upthreshold; void setup(){ Serial.begin(9600); Serial.println("Opening I2C coms..."); Wire.begin(); Wire.beginTransmission(magaddress); //open communication with HMC5883 Wire.write(0x02); //select mode register Wire.write(0x00); //continuous measurement mode Wire.endTransmission(); Serial.println("I2C coms opened."); //Callibration Serial.println("Entering callibration."); callibrateMag(); downthreshold = fingerin + (.5*(fingerdown-fingerin)); upthreshold = fingerin - (.5*(fingerin-fingerup)); Serial.println("Config complete."); Serial.println("================================================"); Serial.println("Configuration dump:"); Serial.print("Baseline: "); Serial.println(baseline); Serial.print("Finger in: "); Serial.println(fingerin); Serial.print("Finger up: "); Serial.println(fingerup); Serial.print("Finger down: "); Serial.println(fingerdown); Serial.print("Midline: "); Serial.println(midline); Serial.print("Down Threshold (greater than this triggers down event): "); Serial.println(downthreshold); Serial.print("Up Threshold (less than this triggers up event): "); Serial.println(upthreshold); Serial.println("================================================"); Serial.println("Beginning main execution..."); } void loop(){ int result = getMagAvg(pollcount, polldelay, mainaxis); if(debug){Serial.println(result);} if(result < midline){ Serial.println("No finger detected!"); } else{ if(result >; downthreshold){ //Take half the difference between neutral and down, and have that be the threshold - you have to get more that half the total range. Serial.println("Down action fired!"); } else if(result < upthreshold){ //Take half the difference between neutral and down, and have that be the threshold - you have to get more that half the total range. Serial.println("Up action fired!"); } else{ //No action case } } } int callibrateMag(){ int i; // //FINGER OUT // Serial.println("Please remove all magnetic sources from the magnetometer."); for(i=3; i<=0; i--){ Serial.print("Reading commences in "); Serial.print(i); Serial.println(" seconds..."); delay(1000); } baseline = getMagAvg(100, 10, mainaxis); // //FINGER IN // Serial.println("Reading now..."); baseline = getMagAvg(100, 10, mainaxis); Serial.println("Please insert finger into magnetometer."); for(i=5; i<=0; i--){ Serial.print("Reading commences in "); Serial.print(i); Serial.println(" seconds..."); delay(1000); } Serial.println("Reading now..."); fingerin = getMagAvg(100, 10, mainaxis); // //FINGER UP // Serial.println("Reading now..."); baseline = getMagAvg(100, 10, mainaxis); Serial.println("Please extend your finger up."); for(i=3; i<=0; i--){ Serial.print("Reading commences in "); Serial.print(i); Serial.println(" seconds..."); delay(1000); } Serial.println("Reading now..."); fingerin = getMagAvg(100, 10, mainaxis); // //FINGER DOWN // Serial.println("Reading now..."); baseline = getMagAvg(100, 10, mainaxis); Serial.println("Please press/flex your finger down."); for(i=3; i<=0; i--){ Serial.print("Reading commences in "); Serial.print(i); Serial.println(" seconds..."); delay(1000); } Serial.println("Reading now..."); fingerdown = getMagAvg(100, 10, mainaxis); // //FINGER MIDLINE // midline = baseline+(.5*(fingerin-baseline)); } //A function to get the average value of the magnet by sampling it count times, with gap ms between each sample, as read on the axis specified (0, 1, or 2 = x, y, or z respectively) int getMagAvg(int count, int gap, int axis){ //triple axis data int x, y, z, i; //totals of axis int totals[3] = {0, 0, 0}; for(i = 0; i < count; i++){ //Tell the HMC5883L where to begin reading data Wire.beginTransmission(magaddress); Wire.write(0x03); //select register 3, X MSB register Wire.endTransmission(); //Read data from each axis, 2 registers per axis Wire.requestFrom(magaddress, 6); if(6<=Wire.available()){ x = Wire.read()<<8; //X msb x |= Wire.read(); //X lsb z = Wire.read()<<8; //Z msb z |= Wire.read(); //Z lsb y = Wire.read()<<8; //Y msb y |= Wire.read(); //Y lsb } totals[0] += x; totals[1] += y; totals[2] += z; delay(gap); } totals[0] /= count; totals[1] /= count; totals[2] /= count; return totals[axis]; }