DreamForce 2013 Mini-Hack Challenge Project

Dependencies:   ADXL345 USBDevice mbed filter

Revision:
1:d9d593d4ea39
Parent:
0:a2c33a8eded1
Child:
2:e1c07ecec050
--- 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);
     }
 }