DreamForce 2013 Mini-Hack Challenge Project

Dependencies:   ADXL345 USBDevice filter mbed

Fork of df-minihack-slingshot by Doug Anson

Files at this revision

API Documentation at this revision

Comitter:
ansond
Date:
Fri Nov 01 17:18:40 2013 +0000
Parent:
0:a2c33a8eded1
Child:
2:e1c07ecec050
Commit message:
updates

Changed in this revision

filter.lib Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/filter.lib	Fri Nov 01 17:18:40 2013 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/networker/code/filter/#46a72e790df8
--- 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);
     }
 }