DreamForce 2013 Mini-Hack Challenge Project
Dependencies: ADXL345 USBDevice filter mbed
Fork of df-minihack-slingshot by
Revision 1:d9d593d4ea39, committed 2013-11-01
- 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);
}
}
