Some days ago a friend asked me how to build a device that periodically presses a push button. After some tought I came up with a simple solution using an Arduino and a servo motor.
A servo motor is a motor that can precisely position his actuator arm. To position the actuator arm the device that drives the servo motor must use a PWM signal; most RC servo motors work with a 50Hz frequency (20 mS pulse) with a positive pulse ranging from 1 to 2 mS.
This means that if we want to use Arduino to generate such a waveform, we must do some trick because the Arduino analogWrite generates a 490Hz PWM waveform.
The length of the positive pulse sets the actuator arm position.
The servo I’ve used in this project is an EK2-508 spare part for my wonderful E-Sky Co-Comanche helicopter; it’s cheap and easy to find, but any other 5V servo should be ok.
To precisely regulate the actuator arm positioning I’ve used a 100K trimmer that generates a 0-5V signal. The Arduino reads the value through the A0 analog input and generates a positive pulse proportional to the analog value.
Be careful !! to get this image I’ve used Fritzing but the Arduino Nano of the Fritzing library has a different pinout from the Arduino board I’ve got. So carefully check the pinout of your Arduino, don’t trust the image.
In this video You can see the device in action: a green led lights up when the pushbutton is pressed.
The software is pretty straightforward. The PWM pulse is obtained “by hand” using the digitalWrite function. The only thing to notice is that as the positive pulse width ranges from 1 to 2 mS I’ve used the delayMicroseconds to get the maximum resolution.
For the remaining part of the pulse (always LOW) I’ve used a 18mS delay, even because the delayMicroseconds does not generate such long intervals.
As the servo motor tolerates a ± 2mS wrong timing there is no need to consider the timing of the software statements that generate the pulses.
#define SERVO_PIN 4
#define TRIMMER_PIN A0
#define PUSH_TIME_MS 200
#define TIME_BETWEEN_PUSHES_MS 3000
#define MAX_POSITIVE_PULSE_TIME_uS 2000
#define MIN_POSITIVE_PULSE_TIME_uS 1000
void setup()
{
pinMode(SERVO_PIN, OUTPUT);
//read the ADC once discard the value and wait.
//the first read can be wrong
analogRead(TRIMMER_PIN);
delay(300);
//Debug log on Serial
Serial.begin(9600);
}
void SetServo(unsigned int pHighPulseWidthInMillis)
{
//The 20 mS pulse generation is splitted in two parts:
//1) the first 2 mS
//2) the remaining 18 mS
//This part generates the first 2 mS of the PWM pulse
//High Pulse
digitalWrite(SERVO_PIN, HIGH);
delayMicroseconds(pHighPulseWidthInMillis);
//Low pulse (up to 2000uS)
digitalWrite(SERVO_PIN, LOW);
delayMicroseconds(MAX_POSITIVE_PULSE_TIME_uS -
pHighPulseWidthInMillis);
//As the delayMicroseconds cannot be used for more than 16384 uS
//we use delay for the 18mS low pulse
delay(18);
}
void loop()
{
for(;;)
{
unsigned long t;
unsigned int pulseUs;
unsigned int analogValue;
//Read from the trimmer the Positive Pulse duration (0..1023)
//and convert to a (0..1000) value
analogValue = (double)analogRead(TRIMMER_PIN) / 1023.0 *
(MAX_POSITIVE_PULSE_TIME_uS - MIN_POSITIVE_PULSE_TIME_uS);
Serial.print("Trimmer Value --> ");
Serial.println(analogValue);
//Add the minimum positive pulse time
pulseUs = MIN_POSITIVE_PULSE_TIME_uS + analogValue;
Serial.print("Positive Pulse Width (uS) --> ");
Serial.println(pulseUs);
//Set the pushed servo position for PUSH_TIME_MS
for(t = millis() + PUSH_TIME_MS; t > millis();)
SetServo(pulseUs);
//Set the idle servo position for TIME_BETWEEN_PUSHES_MS
for(t = millis() + TIME_BETWEEN_PUSHES_MS; t > millis();)
SetServo(MAX_POSITIVE_PULSE_TIME_uS);
}
}
Stay tuned I’m cooking something else
