DreamForce 2013 Mini-Hack Challenge Project
Dependencies: ADXL345 USBDevice mbed filter
Diff: main.cpp
- Revision:
- 1:d9d593d4ea39
- Parent:
- 0:a2c33a8eded1
- Child:
- 2:e1c07ecec050
diff -r a2c33a8eded1 -r d9d593d4ea39 main.cpp --- a/main.cpp Wed Oct 30 19:07:09 2013 +0000 +++ b/main.cpp Fri Nov 01 17:18:40 2013 +0000 @@ -20,6 +20,49 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +// +// DreamForce 2013 Challenge: +// +// Goal: modify the code below to adjust the final angle (theta) to take into account +// the relative angle between the sling body and the sling band. +// +// +// Mini-hack challenge: Your mission, should you choose to accept it, is to complete the function +// "potentiometer_value_to_degrees()" below (around line 129) to return a reasonable +// estimate of the sling band angle relative to the sling body +// +// + +// DreamForce2013 TUNABLES START + +// maximum angular value of the sling band +float max_sling_angle = 30.0; + +// minimum angluar value of the sling band +float min_sling_angle = -30.0; + +// DreamForce2013 TUNABLES END + +// Sling Tunables Start - be careful changing these + +// stretch start threshold +float stretch_start_threshold = 0.4; + +// fire threshold +float fire_threshold = 0.15; + +// fire timing threshold +float fire_timing_threshold = 3.0; + +// scaling for mouse movement +int mouse_scale = 230; + +// Sling Tunables End + +// definition of PI +#define M_PI 3.14159 + +// Includes #include "mbed.h" #include "USBMouse.h" #include "ADXL345.h" @@ -30,19 +73,117 @@ AnalogIn stretch_sensor(p15); BusOut leds(LED1, LED2, LED3, LED4); +// Potentiometers +AnalogIn pot_1(p19); +//AnalogIn pot_2(p20); + +// keep track of mouse position +int current_x = 0; +int current_y = 0; + +// Potentiometer filters +#include "filter.h" +medianFilter prefilter(13); +medianFilter postfilter(7); + +// return radians for a given degree +float degrees_to_radians(float degrees) { + float radians = ((M_PI*degrees)/180.0); + return radians; +} + +// return degrees for a given radian +float radians_to_degrees(float radians) { + float degrees = ((180*radians)/M_PI); + return degrees; +} + +// return an average of two degrees +float compute_average(float deg1, float deg2) { + return (float)((deg1+deg2)/2); +} + +// +// DreamForce 2013 Challenge: +// get_potentiometer_value() reads the potentiometer, filters its value and remaps it to [0, 100.0] +// +float get_potentiometer_value(AnalogIn pot) { + float f = pot; + f = prefilter.process(f); + f = (f * 100); // [ 0, 100] + return postfilter.process(f); +} + // // DreamForce 2013 Challenge: -// - Adjust the resultant slingshot angle for the difference between the accelerometer angle and the relative angle of the sling to the slingshot body. -// - Result should be in radians: up > 0 > down just like with get_angle() below - except adjusted! -// - Note: You may need to add some filters to optimize (and stabilize) the readings from the potentiometers -// -float adjust_for_sling_angle(float slingshot_body_angle) { - float modified_angle = slingshot_body_angle; // default +// potentiometer_value_to_degrees() takes the potentiometer value (val_pot) and +// maps it to an angle between [min_sling_angle, max_sling_angle] as defined in the tunables section +// +// NOTE: This function is INCOMPLETE. To complete it you should: +// 1). Uncomment the debug statement, run the program, and look at raw potentiometer values +// 2). Determine the min and max potentiometer values you wish to scale to degrees +// 3). Determine the 90 degree potentiometer value ("median_pot") that denotes the sling band at 90 to the sling body +// 4). Fill in median_pot, min_pot, max_pot below +// 5). Compile up and give it a try +// +float potentiometer_value_to_degrees(float val_pot) { + float deg = 0.0; + float accuracy = 0.1; + + // DEBUG - may need this to calibrate pot values below + //std::printf("Raw pot value=%.1f\r\n",val_pot); + + // Potentiometer range: you need to uncomment the debug statement above and determine the following values + float median_pot = 0; + float min_pot = 0; + float max_pot = 0; + float incr_pot = (max_pot*10) - (min_pot*10); // how many .1 increments we have in the interval [min, max] + + // Mapped degree range: approx [min_sling_angle, max_sling_angle] degrees so convert to + float min_deg = min_sling_angle; + float max_deg = max_sling_angle; + float incr_deg = (max_deg*10) - (min_deg*10); // how many .1 increments we have in the interval [min, max] - // innovate!!! + // see if we are centered or not + float centered_pot = fabs(val_pot - median_pot); + + // if we are off 90 degrees (i.e. sling body and sling band are not at 90 degrees) - calculate the relative angle + if (centered_pot > accuracy) { + // map to degree range + float conversion = (incr_deg/incr_pot); + deg = min_deg + (conversion*(val_pot - min_pot)); + } + + // we have to flip the sign of the result + deg = -deg; - // return the modified angle taking into account the sling angle relative to the slingshot body - return modified_angle; + // return the calculated degrees + return deg; +} + +// adjust the final angle (theta) taking into account the relative angle between the sling body and the sling band. +float adjust_for_sling_angle(float slingshot_body_angle) { + // get the sling angle through approximation with the potentiometer, averaged from both potentiometers + float sling_angle_degrees = compute_average(potentiometer_value_to_degrees(get_potentiometer_value(pot_1)),potentiometer_value_to_degrees(get_potentiometer_value(pot_1))); + + // the sling angle is in degrees - so lets convert the body angle to degrees as well + float modified_angle_degrees = radians_to_degrees(slingshot_body_angle); + + // we simply add the sling angle to adjust it + modified_angle_degrees += sling_angle_degrees; + + // make sure that we are always between 0 and 359 degrees + while (modified_angle_degrees > 360.0) modified_angle_degrees = modified_angle_degrees - 360; + while (modified_angle_degrees < 0.0) modified_angle_degrees = modified_angle_degrees + 360; + + // convert the modified angle back to radians + float modified_angle_radians = degrees_to_radians(modified_angle_degrees); + + // DEBUG + //std::printf("adjust_for_sling_angle: body_angle=%.1f sling_angle=%.1f modified_angle=%.1f\r\n",radians_to_degrees(slingshot_body_angle),sling_angle_degrees,modified_angle_degrees); + + // return the modified angle + return modified_angle_radians; } // Return slingshot angle in radians, up > 0 > down @@ -68,8 +209,6 @@ // move mouse to a location relative to the start point, stepping as needed void move_mouse(int x, int y) { const int STEP = 10; - static int current_x = 0; - static int current_y = 0; int move_x = x - current_x; int move_y = y - current_y; @@ -79,11 +218,18 @@ while(move_x < -STEP) { mouse.move(-STEP, 0); move_x += STEP; } while(move_y > STEP) { mouse.move(0, STEP); move_y -= STEP; } while(move_y < -STEP) { mouse.move(0, -STEP); move_y += STEP; } - mouse.move(move_x, move_y); + mouse.move(move_x, move_y); current_x = x; current_y = y; } + +// reset the mouse position +void reset_mouse() { + current_x = 0; + current_y = 0; + mouse.move(0,0); +} template <class T> T filter(T* array, int len, T value) { @@ -104,7 +250,11 @@ } state_t; int main() { + bool loop_forever = true; leds = 1; + + // init mouse tracking + reset_mouse(); // setup accelerometer accelerometer.setPowerControl(0x00); @@ -118,8 +268,7 @@ float angles[8] = {0}; float stretches[8] = {0}; - while(1) { - + while(loop_forever) { // get the slingshot parameters float this_stretch = get_stretch(); float this_angle = get_angle(); @@ -132,38 +281,46 @@ angle = adjust_for_sling_angle(angle); leds = state; - + // act based on the current state switch (state) { case WAITING: - if(stretch > 0.5) { // significant stretch, considered starting + if(stretch > stretch_start_threshold) { // significant stretch, considered starting mouse.press(MOUSE_LEFT); state = AIMING; } break; case AIMING: - if(stretch - this_stretch > 0.1) { // rapid de-stretch, considered a fire + if(stretch - this_stretch > fire_threshold) { // rapid de-stretch, considered a fire mouse.release(MOUSE_LEFT); - move_mouse(0, 0); + reset_mouse(); timer.start(); state = FIRING; + } + else if(stretch < stretch_start_threshold) { // de-stretch + reset_mouse(); + timer.stop(); + timer.reset(); + state = WAITING; } else { - int x = 0.0 - cos(angle) * stretch * 200; - int y = sin(angle) * stretch * 200; + int x = 0.0 - cos(angle) * stretch * mouse_scale; + int y = sin(angle) * stretch * mouse_scale; move_mouse(x, y); } break; case FIRING: - if(timer > 3.0) { + if(timer > fire_timing_threshold) { timer.stop(); timer.reset(); + reset_mouse(); state = WAITING; } break; }; - wait(0.01); + // wait for 100ms + wait_ms(100); } }