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.
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.
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.