Erroneous Accelerometer to Servo Interface

I am working on a project in which I want to control a servo using the tilt readings of an accelerometer (Memsic 2125). As of right now I am not too concerned about the tilt angle to servo rotation ratio. I would simply like the servo to rotate consistently with the tilt of the accelerometer.

My attempt at this is as follows:

#include <Servo.h>

int ypin = 3;
Servo servo1;

void setup()
{
  pinMode(ypin, INPUT);
  servo1.attach(10);
}

void loop()
{
  int val = digitalRead(3);  
  val = map(val, 1775, 3125, 0, 180);  //accelerometer to servo conversion
  digitalWrite(10, val);  
}

If anyone has any suggestions as to how I can make this work, I would greatly appreciate their ideas.

Memsic 2125 Datasheets:
http://www.parallax.com/Portals/0/Downloads/docs/prod/acc/memsickit.pdf

http://www.parallax.com/Portals/0/Downloads/docs/prod/acc/MXD2125G&M.pdf

There is example code for the memsic 2125 here: http://arduino.cc/en/Tutorial/Memsic2125?from=Tutorial.AccelerometerMemsic2125

You may want to smooth the output of the accelerometer before mapping it to a servo angle.

Here are two examples of smoothing:

http://www.arduino.cc/playground/Main/Smooth

google is your friend :wink:

Yeah, the main problem is that you're using digitalRead to try to get the duty cycle of a PWM input. The example code mem posted will get you there, key concept is using pulseIn.

After reading:

the main problem is that you're using digitalRead to try to get the duty cycle of a PWM input.

I realized my code probably had other problems aside from using digitalRead instead of pulseIn.

This being realized I researched a bit further to find this page:
http://itp.nyu.edu/physcomp/Labs/Servo
Tom Igoe's code is intended for an analog input. I modified it to accept a digital input, and added smoothing. Just as macegr, and mem suggested;

#define NUMREADINGS 10

int readings[NUMREADINGS];   // Readings from the analog input
int index = 0;               // Index of the current reading
int total = 0;               // Running total
int average = 0;             // Initial Average
int servoPin = 3;            // Control pin for servo motor
int accelPin = 2;            // Accelerometer input pin
int minPulse = 0;            // Minimum servo position
int maxPulse = 180;          // Maximum servo position
int pulse = 0;               // Amount to pulse the servo

long lastPulse = 0;          // Time in milliseconds of the last pulse
int refreshTime = 20;        // Time needed in between pulses


void setup() {
  for (int i = 0; i < NUMREADINGS; i++)
    readings[i] = 0;                      // Initialize all the readings to 0
  pinMode(accelPin, INPUT);               // Set accelerometer pin as an input pin
  pinMode(servoPin, OUTPUT);              // Set servo pin as an output pin
 pulse = minPulse;                        // Set the motor position value to the minimum
}

void loop() {
 total -= readings[index];                 // Subtract the last reading
  readings[index] = digitalRead(accelPin); // Read from the sensor
  total += readings[index];                // Add the reading to the total
  index = (index + 1);                     // Advance to the next index

  if (index >= NUMREADINGS)                // If we're at the end of the array...
    index = 0;                             // ...wrap around to the beginning

  average = total / NUMREADINGS;           // Calculate the average
  
  int average = pulseIn(accelPin, HIGH);               // Read the accelerometer input
 pulse = map(average, 90, 110, minPulse, maxPulse);    // Convert the analog value
                                                       // To a range between minPulse and maxPulse

 // pulse the servo again if rhe refresh time (20 ms) have passed:
 if (millis() - lastPulse >= refreshTime) {
   digitalWrite(servoPin, HIGH);           // Turn the motor on
   delayMicroseconds(pulse);               // Length of the pulse sets the motor position
   digitalWrite(servoPin, LOW);            // Turn the motor off
   lastPulse = millis();                   // Save the time of the last pulse
 }
}

This code works well when the accelerometer is rotated between 45 and 80 degrees. Any tilt less than 45 degrees doesn't trigger the servo, and any tilt greater than 80 degrees erroneously triggers the servo. I realize that the memsic 2125 is only accurate between 0 and about 60 degrees, which may be the cause of my >80 degree problems. But why can't I get the servo to rotate between 0 and 45 degrees of tilt?

Might it have something to do with how I have the map set up?
If so, any suggestions on how to correctly set up the map between the accelerometer values and the servo values would be greatly appreciated.

Yes, it probably is the arguments in the map function. Why not experiment with a simpler sketch that takes input from pot or serial port (example code for both can be easily found) and see what values you need to get the servo to move as you want.

It's the map parameters, on the input side. You will be receiving values in microseconds, read the datasheet to calculate how many. And go ahead and remove all the digitalRead stuff that isn't relevant anymore. That code was trying to get the PWM duty cycle with statistical analysis, it would be pretty accurate if you could be assured a fast refresh rate and a larger number of readings...but the pulseIn function is a little more direct.