Hardware components | ||||||
![]() |
| × | 1 | |||
| × | 1 | ||||
![]() |
| × | 1 | |||
| × | 1 | ||||
![]() |
| × | 1 | |||
Software apps and online services | ||||||
![]() |
| |||||
Hand tools and fabrication machines | ||||||
|
The goal of that projet is to create tool, appropriate to test the behaviour of a Servo Motor on an RC Car, a Drone or even in my future R2D2 :-)
What is special about this tool:
- I can set or limit the range of movement
- I can configure it to sweep back-and-forth
- I can monitor the current consumed by the servo motor that is connected
- And, finally, I can set the position of the servo at 90 degrees or even, "in the center" of the two limits
This tool is intended to be used with my robotic projects such as the R2D2 unit that I decided design, 3D print and to build. The R2D2 project will include over 20 servo motors so, once installed, it may be useful to test the servo before screwing everything in place and that is why I created this tool
Video presentation of the projectThe menu that I programmed is as follow:
After booting up, we have access to a menu with different informations
- First, the number "23" on the top left is the actual angle of the servo (on 180 degrees)
- Then, the number 34.00 is the power consumption in milliamp
- On the second line, we can see the limits that have been set: 0-180 are the default
- And, finally, the mode is POT: this mode allows the control of the servo with the potentiometer and the other mode is SWEEP (The sweep mode is design to make the servo move between the limits)
If I push the left button (The Rotary encoder), I have access to the menu options.
Setting Minimum and Maximum range
The first and second options are designed to set the minimum and maximum values of the range.
Changing the mode
The next option is the mode
- SWEEP to trigger the angle change between the MIN and MAX Set values. If no values are set, the servo will SWEEP between 0 and 180 degrees
- POT to control the servo manually via the Potentiometer
The Center option
And finally the CENTER function allows the servo to calculate its position between the MIN and MAX. The limits could have been changed or left at the default values (0-180), the CENTER will always position the servo in the middle
The code is available in the appropriate section
/*
--- mesure du courant consomme
--- reverse direction
--- determiner et configurer les limites
--- afficher les informations pertinentes
--- potentiometre pour faire bouger le servo
--- auto sweep en fonction des limites etablies
--- regulateur de voltage variable - 5 ou 6v
-- find replace version: v.305.d
/**************************
Pins: Arduino
GND = GND
VCC = 3.3 or 5V
SCL = A5
SDA = A4
***************************/
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
Adafruit_SSD1306 display(128, 64);
// SERVO
#include <Servo.h>
Servo ServoToTest; // create servo object to control a servo
int pot_PIN = A2; // analog pin used to connect the potentiometer
int ServoPosition = 0; // variable to store the servo position
int ServoDirection = 0;
int ServoCenter = 0;
int ServoMode = 0; // 0 = MODE-POT - 1 = SWEEP
int ServoMINAngle = 0;
int ServoMAXAngle = 180;
//Current
const int MAX471_PIN = A1; // current sensor
int MAX471_RawValue = 0;
float MAX471_Current = 0;
// Moving Average CURR
const int CURR_num_readings = 5;
int CURR_readings[CURR_num_readings]; // the readings from the analog input
int CURR_readIndex = 0; // the index of the current reading
float CURR_total = 0; // the running total
unsigned long previousCURR_Read = 0; // will store last time LED was updated
const long intervalBetweenCURR_Read = 2; // interval at which to blink (milliseconds)
// Moving Average POT
const int POT_num_readings = 3;
int POT_readings[POT_num_readings]; // the readings from the analog input
int POT_readIndex = 0; // the index of the current reading
float POT_total = 0; // the running total
// pour trouver les dernieres values MIN et MAX
const int numReadingzs = 50;
int Readingzs[numReadingzs]; // the Readingzs from the analog input
int reaZIndex = 0; // the index of the current Readingz
// ROTARY ENCODER
// https://github.com/RalphBacon/RotaryEncoderUpdate
const int Rotary_CLKPin = 2;
const int Rotary_DTAPin = 4;
const int Rotary_SW_Pin = 3;
int Rotary_ButtonState = 0; // the current state of the output pin
// Keep track of last rotary value
int Rotary_Value = 0;
// Updated by the Rotary_isr (Interrupt Service Routine)
volatile int Rotary_virtualPosition = 0;
// ------------------------------------------------------------------
// SETUP SETUP SETUP SETUP SETUP SETUP SETUP
// ------------------------------------------------------------------
void setup()
{
Serial.begin(9600);
pinMode(MAX471_PIN, INPUT);
pinMode(pot_PIN, INPUT);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3C (for the 128x32)
display.clearDisplay();
// ROTARY ENCODER
// Rotary pulses are INPUTs
pinMode(Rotary_CLKPin, INPUT);
pinMode(Rotary_DTAPin, INPUT);
pinMode(Rotary_SW_Pin, INPUT_PULLUP);
// Attach the routine to service the interrupts
attachInterrupt(digitalPinToInterrupt(Rotary_CLKPin), Rotary_isr, LOW);
for (int i = 0; i < numReadingzs; i++)
Readingzs[i] = 0;
header();
display.setCursor(0, 15);
display.setTextSize(1);
display.print ("Version 305d");
display.setCursor( 0, 25);
display.print ("Science 3D");
refresh();
delay(3000);
ServoToTest.attach(5); // attaches the servo on pin 9 to the servo object
}
// ------------------------------------------------------------------
// MAIN LOOP MAIN LOOP MAIN LOOP MAIN LOOP MAIN LOOP
// ------------------------------------------------------------------
void loop()
{
// ROTARY ENCODER
Rotary_PushButton();
if (Rotary_ButtonState == 0)
{
if (Rotary_Value == 0)
{
ServoMINAngle = ServoPosition;
Rotary_Value = -1;
}
if (Rotary_Value == 1)
{
ServoMAXAngle = ServoPosition;
Rotary_Value = -1;
}
if (Rotary_Value == 2)
{
ServoMode = 1;
Rotary_Value = -1;
}
if (Rotary_Value == 3)
{
ServoMode = 0;
Rotary_Value = -1;
}
if (Rotary_Value == 4) // center pot
{
ServoMode = 0;
ServoCenter = 1;
ServoToTest.write(180 - ((ServoMINAngle + ServoMAXAngle) / 2));
for (int i = 0; i < 50; i++)
MovingAVG_POT(90);
Rotary_Value = -1;
}
if (Rotary_Value == 5)
{
Rotary_Value = -1;
}
if (Rotary_Value == 6)
{
for (int i = 0; i < 50; i++)
MovingAVG_POT(0);
ServoMINAngle = 0;
ServoMAXAngle = 180;
ServoCenter = 0;
ServoMode = 0;
Rotary_Value = -1;
}
// unsigned long currentMillis = millis();
// if (currentMillis - previousCURR_Read >= intervalBetweenCURR_Read)
// {
// previousCURR_Read = currentMillis;
// ReadCurrent();
// }
ReadCurrent();
header();
display.setCursor(0, 11);
display.setTextSize(2);
if (ServoCenter == 1)
display.print ((ServoMINAngle + ServoMAXAngle) / 2);
else
display.print (ServoPosition);
display.setTextSize(1);
display.setCursor( 65, 11);
display.print ("AVG mAMP");
display.setCursor( 65, 19);
display.print (MAX471_Current);
display.drawLine (0, 28, 127, 28, WHITE);
display.setCursor(0, 32);
display.print ("SERVO MIN");
display.setCursor( 65, 31);
display.print ("SERVO MAX");
display.setCursor( 0, 40);
display.print (ServoMINAngle);
display.setCursor( 65, 40);
display.print (ServoMAXAngle);
display.drawLine (0, 49, 127, 49, WHITE);
display.setCursor(32, 51);
if (ServoMode == 0)
{
if (ServoCenter == 0)
ServoKnob();
display.print ("MODE: POT");
}
else if (ServoMode == 1)
{
if (ServoCenter == 0)
ServoSweep();
display.print ("MODE: SWEEP");
}
//Adafruit_SSD1306 display(128, 64);
// (x,y,w,h,color)
display.fillRect(0, 0, 10, 9, WHITE);
display.fillRect(118, 0, 12, 9, WHITE);
display.fillRect(0, 50, 30, 15, BLACK);
display.fillRect(98, 50, 30, 15, BLACK);
refresh();
}
else if (Rotary_ButtonState == 1 && ServoCenter == 1)
{
ServoCenter = 0;
}
else if (Rotary_ButtonState == 1 && ServoCenter == 0)
{
Rotary_TurnButton();
if (Rotary_Value == 0 )
{
header();
display.setCursor(0, 11); display.print (">-SERVO MIN ---<");
display.setCursor(0, 20); display.print (" SERVO MAX");
display.setCursor(0, 29); display.print (" MODE-SWEEP");
display.setCursor(0, 38); display.print (" MODE-POT");
display.setCursor(0, 47); display.print (" CENTER");
display.setCursor(0, 56); display.print (" BACK");
refresh();
}
else if (Rotary_Value == 1)
{
header();
display.setCursor(0, 11); display.print (" SERVO MIN");
display.setCursor(0, 20); display.print (">-SERVO MAX ---<");
display.setCursor(0, 29); display.print (" MODE-SWEEP");
display.setCursor(0, 38); display.print (" MODE-POT");
display.setCursor(0, 47); display.print (" CENTER");
display.setCursor(0, 56); display.print (" BACK");
refresh();
}
else if (Rotary_Value == 2)
{
header();
display.setCursor(0, 11); display.print (" SERVO MIN");
display.setCursor(0, 20); display.print (" SERVO MAX");
display.setCursor(0, 29); display.print (">-MODE-SWEEP --<");
display.setCursor(0, 38); display.print (" MODE-POT");
display.setCursor(0, 47); display.print (" CENTER");
display.setCursor(0, 56); display.print (" BACK");
refresh();
}
else if (Rotary_Value == 3)
{
header();
display.setCursor(0, 11); display.print (" SERVO MIN");
display.setCursor(0, 20); display.print (" SERVO MAX");
display.setCursor(0, 29); display.print (" MODE-SWEEP");
display.setCursor(0, 38); display.print (">-MODE-POT ---<");
display.setCursor(0, 47); display.print (" CENTER");
display.setCursor(0, 56); display.print (" BACK");
refresh();
}
else if (Rotary_Value == 4)
{
header();
display.setCursor(0, 11); display.print (" SERVO MIN");
display.setCursor(0, 20); display.print (" SERVO MAX");
display.setCursor(0, 29); display.print (" MODE-SWEEP");
display.setCursor(0, 38); display.print (" MODE-POT");
display.setCursor(0, 47); display.print (">-CENTER -----<");
display.setCursor(0, 56); display.print (" BACK");
refresh();
}
else if (Rotary_Value == 5)
{
header();
display.setCursor(0, 11); display.print (" SERVO MIN");
display.setCursor(0, 20); display.print (" SERVO MAX");
display.setCursor(0, 29); display.print (" MODE-SWEEP");
display.setCursor(0, 38); display.print (" MODE-POT");
display.setCursor(0, 47); display.print (" CENTER");
display.setCursor(0, 56); display.print (">-BACK -------<");
refresh();
}
else if (Rotary_Value == 6)
{
header();
display.setCursor(0, 11); display.print (" SERVO MAX");
display.setCursor(0, 20); display.print (" MODE-SWEEP");
display.setCursor(0, 29); display.print (" MODE-POT");
display.setCursor(0, 38); display.print (" CENTER");
display.setCursor(0, 47); display.print (" BACK");
display.setCursor(0, 56); display.print (">-RESET ------<");
refresh();
}
}
//end loop
}
// ------------------------------------------------------------------
// FUNCTION FUNCTION FUNCTION FUNCTION FUNCTION
// ------------------------------------------------------------------
void header()
{
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(18, 0); display.print("Servo Controller");
display.drawLine (0, 9, 127, 9, WHITE);
}
void refresh()
{
display.display();
delay(00);
display.clearDisplay();
}
void ServoSweep()
{
float yy = map(analogRead(pot_PIN), 1022, 0, 1, 10); // scale it to use it with the servo (value between 0 and 180)
if (ServoPosition > ServoMAXAngle)
{
ServoPosition = ServoMAXAngle;
delay(10);
ServoDirection = 0; // on redescend
}
if (ServoPosition < ServoMINAngle)
{
ServoPosition = ServoMINAngle;
delay(10);
ServoDirection = 1; // on remont
}
if (ServoDirection == 1) // on monte
{
if ((ServoPosition + (yy / 2)) >= ServoMAXAngle)
yy = abs(ServoMAXAngle - ServoPosition) / 3 ;
if (yy < 1) yy = 1;
ServoPosition = ServoPosition + yy;
ServoToTest.write(180 - ServoPosition); // tell servo to go to position in variable 'pos'
}
else if (ServoDirection == 0) // on descend
{
if ((ServoPosition - (yy / 2)) <= ServoMINAngle)
yy = abs(ServoPosition - ServoMINAngle) / 3 ;
if (yy < 1) yy = 1;
ServoPosition = ServoPosition - yy;
ServoToTest.write(180 - ServoPosition); // tell servo to go to position in variable 'pos'
}
}
void ServoKnob()
{
ServoPosition = analogRead(pot_PIN); // reads the value of the potentiometer (value between 0 and 1023)
ServoPosition = map(ServoPosition, 1022, 0, 0, 180); // scale it to use it with the servo (value between 0 and 180)
ServoPosition = MovingAVG_POT(ServoPosition);
if (ServoPosition > ServoMAXAngle)
ServoPosition = ServoMAXAngle;
if (ServoPosition < ServoMINAngle)
ServoPosition = ServoMINAngle;
ServoToTest.write(180 - ServoPosition); // sets the servo position according to the scaled value
}
void ReadCurrent()
{ // read current from the MAX471 board
float maIN = analogRead(MAX471_PIN); // read the MAX471_Board
maIN = maIN + 0.001;
maIN = (((maIN * 5.0000 ) / 1024.0) * 1000); // calculate the moving average
float CURR_average = 0; // the average
// subtract the last reading:
CURR_total = CURR_total - CURR_readings[CURR_readIndex];
// read from the sensor:
CURR_readings[CURR_readIndex] = maIN;
// add the reading to the CURR_total:
CURR_total = CURR_total + CURR_readings[CURR_readIndex];
// advance to the next position in the array:
CURR_readIndex = CURR_readIndex + 1;
// if we're at the end of the array...
if (CURR_readIndex >= CURR_num_readings)
CURR_readIndex = 0;
// calculate the CURR_average:
CURR_average = CURR_total / CURR_num_readings;
// send it to the computer as ASCII digits
MAX471_Current = CURR_average;
Readingzs[reaZIndex] = MAX471_Current;
reaZIndex = reaZIndex + 1;
// if we're at the end of the array...
if (reaZIndex >= numReadingzs)
reaZIndex = 0;
}
float MovingAVG_POT(int ValueToAVG)
{
float POT_average = 0; // the average
// subtract the last reading:
POT_total = POT_total - POT_readings[POT_readIndex];
// read from the sensor:
POT_readings[POT_readIndex] = ValueToAVG;
// add the reading to the POT_total:
POT_total = POT_total + POT_readings[POT_readIndex];
// advance to the next position in the array:
POT_readIndex = POT_readIndex + 1;
// if we're at the end of the array...
if (POT_readIndex >= POT_num_readings)
POT_readIndex = 0;
// calculate the POT_average:
POT_average = POT_total / POT_num_readings;
// send it to the computer as ASCII digits
return POT_average;
}
void Rotary_TurnButton()
{
// If the current rotary switch position has changed then update everything
if (Rotary_virtualPosition != Rotary_Value)
{
//Serial.println(Rotary_virtualPosition);
// Keep track of this new value
Rotary_Value = Rotary_virtualPosition ;
}
}
void Rotary_PushButton()
{
// Is someone pressing the rotary switch?
if ((!digitalRead(Rotary_SW_Pin)))
{
Rotary_virtualPosition = 0;
while (!digitalRead(Rotary_SW_Pin))
delay(10); // debounce
Rotary_ButtonState = !Rotary_ButtonState;
}
}
// ------------------------------------------------------------------
// INTERRUPT INTERRUPT INTERRUPT INTERRUPT INTERRUPT
// ------------------------------------------------------------------
void Rotary_isr ()
{
static unsigned long lastInterruptTime = 0;
unsigned long interruptTime = millis();
// If interrupts come faster than 5ms, assume it's a bounce and ignore
if (interruptTime - lastInterruptTime > 5)
{
if (digitalRead(Rotary_DTAPin) == LOW)
Rotary_virtualPosition-- ; // Could be -5 or -10
else
Rotary_virtualPosition++ ; // Could be +5 or +10
// Restrict value from 0 to +100
Rotary_virtualPosition = min(6, max(0, Rotary_virtualPosition));
}
// Keep track of when we were here last (no more than every 5ms)
lastInterruptTime = interruptTime;
}
Comments
Please log in or sign up to comment.