Science 3D
Published © GPL3+

Smart Servo Motor Tester with mAmp meter

This is my idea of a smart Servo controller as a TOOL to help me with my Remote Control Cars, planes and R2D2

IntermediateShowcase (no instructions)4 hours1,424
Smart Servo Motor Tester with mAmp meter

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
MAX471 3A Range Votage Current Sensor
×1
Rotary Encoder with Push-Button
Rotary Encoder with Push-Button
×1
I2C Level 5V-3V Conversion translator shifter Module
×1
JLCPCB Customized PCB
JLCPCB Customized PCB
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Formlabs Form 2 SLA 3D Printer

Story

Read more

Custom parts and enclosures

Enclosure top

Enclosure bottom

Schematics

Eagle grbl files

Code

The source code

Arduino
/*
  --- 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;
}

Credits

Science 3D
11 projects • 25 followers
I have a background in electronics, since 1984. I used to work as a programmer for a couple of years. Now, I am working as a DBA.
Contact

Comments

Please log in or sign up to comment.