Replacing Servo Feedback Resistor to digital control

Hello Everyone, longtime reader first time user.

I didn't know where else to turn and since this is such a knowledgeable forum, I figured I could at least try here.

I'm working on a robot arm. Its not my first one, but on this one I would like much more precise control of these Servos(Link), as well as hopefully eliminate those servo 'jumps' at power on. It seems that any length of time between uses are enough to throw my other robot arms off to where they wont grab something larger like a lighter off my desk without adjustment.

I plan to use the AS5600 magnetic encoder for position feedback. I have removed the internal feedback potentiometer from the servo, and ran the 3 wires out. I am currently using a DAC MCP4725, but without great success. In respect to where the internal pot was soldered, I now have the DACs' output ground to that ground, and the OUT of the DAC to where the wiper terminal of the Servos old pot was, but I'm not sure what to do with that +3.3V that went to the servos old pot, and so currently it is disconnected/floating.

I also have the standard 3-wires of the servo on a separate 5V power supply (with joining grounds between MCU and Servo Power) - and the Servo Control wire to the MCU pin.

Counterclockwise, the servo works great. Very smooth (there is some occasional jitter in there, see below) and normal wattage used. Goes through all speeds min to max quite well.

But clockwise is so jittery from about 1/4 of the max speed up through about 3/4 speed- and you can hear the power surges in the motor as well as see it on the voltage meter. The range is also a lot less from stop to max speed, like half (give or take) of counterclockwise range.

I have tried a lot of different things without much improvement. Tried a PID controller for a few months without success. I'm hoping to run across someone that has tried something similar or has an idea or suggestion.

I will also post my code below, just in case. Its nothing special, it just allows me to use the Serial Terminal to send either the DAC or the Servo PWM values to control the output speed of the servo.

#include "Wire.h"
#include "MCP4725.h"

MCP4725 MCP(0x60);

const int NumOfChars = 150;
char buf[NumOfChars];
char ThisMsg[NumOfChars];

int servoPin = 3;
byte servoChannel = 1;
int servoFreq = 100;
int servoResolution = 12;
int servoStopSpd = 613;
int servoVal = servoStopSpd;
int DACStopSpd = 2048;
int DACVal = DACStopSpd;
int Mode = 1;   // enables between DAC output (by typing "c" in Serial Mon.) or Servo Speed Control ("s")
bool SerialMessage;
int oldServoRead;
int oldDACRead;
unsigned long lastDACSend;
unsigned long lastServoSend;
bool Findmode = 0;    //enables to slowly ramp up or down to find speeds at all ranges

void setup()
{
  Serial.begin(115200);
  unsigned long last = millis() - 500;

  //Wait until an input is typed into serial monitor - solves my ESP32 C3 reboot and serial monitor problem
  while (!SerialMessage)
  {
    WriteSerial();
    if (millis() - last >= 500)
    {
      Serial.println("Type Anything to proceed");
      last = millis();
    }
  }
  SerialMessage = 0;

  Serial.println();
  Serial.println("Beginning Setup");
  Serial.println(F("Sketch Location: "__FILE__ ));
  Serial.println(F("Install Date/Time: "__DATE__ " " __TIME__));  // Print file location and time

  Wire.begin(1, 0);
  //Wire.setClock(400000);
  MCP.begin();
  WriteDAC();
  ledcAttachChannel(servoPin, servoFreq, servoResolution, servoChannel);
  WriteServo();
  Serial.println();
  Serial.println("Setup Complete");
  Serial.println();
}



void loop()
{
  WriteSerial();
  DoStuffWMessage();
  WriteServo();
  WriteDAC();
}




//Changes the DAC Values
void WriteDAC()
{
  int DACRead = MCP.getValue();

  //if (millis() - lastDACSend >= 2)   // trying delays here
  {
    lastDACSend = millis();

    if (DACRead != DACVal)
    {
      if (Findmode) //type "f" in Serial Monitor to change Findmode State
      {
        if (DACRead > DACVal)
          MCP.setValue(DACRead - 1);
        else if (DACRead < DACVal)
          MCP.setValue(DACRead + 1);
      }

      else
        MCP.setValue(DACVal);
    }
  }

  if (DACRead != oldDACRead)
  {
    oldDACRead = DACRead;
    sprintf(buf, "DACRead: %d", DACRead);
    Serial.println(buf);
  }
}



// Change the Servo PWM Values
void WriteServo()
{
  int ServoRead = ledcRead(servoPin);

  //if (millis() - lastServoSend >= 2)  // trying delays here
  {
    lastServoSend = millis();

    if (ServoRead != servoVal)  //type "f" in Serial Monitor to change Findmode State
    {
      if (Findmode)
      {
        if (ServoRead > servoVal)
          ledcWrite(servoPin, ServoRead - 1);
        else if (ServoRead < servoVal)
          ledcWrite(servoPin, ServoRead + 1);
      }

      else
        ledcWrite(servoPin, servoVal);
    }
  }

  if (ServoRead != oldServoRead)
  {
    oldServoRead = ServoRead;
    sprintf(buf, "ServoRead: %d   servoVal: %d", ServoRead, servoVal);
    Serial.println(buf);
  }
}




// Stores Serial Monitor messages for use in DoStuffWMessage
void WriteSerial() {
  while (Serial.available() > 0) {
    static unsigned int message_pos = 0;
    char inByte = Serial.read();

    if (inByte != '\n' && (message_pos < NumOfChars - 1)) {
      ThisMsg[message_pos] = inByte;
      message_pos++;
    }

    else {
      ThisMsg[message_pos] = '\0';
      message_pos = 0;
      SerialMessage = 1;
    }
  }
}



// Type messages in Serial monitor and take action as needed
void DoStuffWMessage() {
  char DoStuffBuffer[NumOfChars] = {};

  if (SerialMessage) {
    size_t msglen = strlen(ThisMsg);
    char str2[] = "0123456789";
    int DigitPos = strcspn(ThisMsg, str2);  // Look for any numbers in the message and show what position
    if (DigitPos >= msglen)                 // If there are no numbers in the message
      DigitPos = -1;                        // Set to -1 for (No Digits)

    char msgtxt[150];  // Create a place to store the text-only
    strcpy(msgtxt, ThisMsg);
    // Save as Text Only by replacing the first number with a null terminator, this will stop the read at that position:
    msgtxt[DigitPos] = '\0';

    int msgdigits = 0;
    if (DigitPos != -1)                      // If there are digits in the message
      msgdigits = atoi(ThisMsg + DigitPos);  // Save the Numbers Only to an Integer

    if (DigitPos == 0) {  //Only a number was entered
      if (Mode == 0)  //DAC Mode
        DACVal = msgdigits;

      if (Mode == 1)  //Servo Mode
        servoVal = msgdigits;
    }

    if (strcmp(ThisMsg, "") == 0 || strcmp(ThisMsg, "stop") == 0)  // Pressing Enter is a quick way to stop the servo
    {
      DACVal = DACStopSpd;
      servoVal = servoStopSpd;
      sprintf(DoStuffBuffer, "Stopped - Servo %d  DAC: %d", servoStopSpd, DACStopSpd);
    }

    if (strcmp(ThisMsg, "+") == 0) {
      if (Mode == 0)
        DACVal++;

      if (Mode == 1)
        servoVal++;
    }

    if (strcmp(ThisMsg, "-") == 0) {
      if (Mode == 0)
        DACVal--;

      if (Mode == 1)
        servoVal--;
    }

    if (strcmp(ThisMsg, "f") == 0) {  //enable findmode to ramp up or down to each position
      Findmode = !Findmode;
      sprintf(DoStuffBuffer, "Findmode: %d", Findmode);
    }

    if (strcmp(msgtxt, "servstop") == 0) {
      servoStopSpd = msgdigits;
      sprintf(DoStuffBuffer, "servoStopSpd: %d", servoStopSpd);
    }

    if (strcmp(msgtxt, "dacstop") == 0) {
      DACStopSpd = msgdigits;
      sprintf(DoStuffBuffer, "DACStopSpd: %d", DACStopSpd);
    }

    if (strcmp(ThisMsg, "s") == 0) {  // enables between DAC output control or Servo Speed Control
      Mode = 1;
      sprintf(DoStuffBuffer, "Servo Mode");
    }

    if (strcmp(ThisMsg, "d") == 0) {  // enables between DAC output control or Servo Speed Control
      Mode = 0;
      sprintf(DoStuffBuffer, "DAC Mode");
    }

    if (strcmp(msgtxt, "frq") == 0) {
      servoFreq = msgdigits;
      ledcChangeFrequency(servoPin, servoFreq, servoResolution);
      sprintf(buf, "Servo Frequency: %d", servoFreq);
    }

    if (strcmp(msgtxt, "res") == 0) {
      servoResolution = msgdigits;
      ledcChangeFrequency(servoPin, servoFreq, servoResolution);
      sprintf(buf, "Servo Resolution: %d", servoResolution);
    }

    if (strcmp(DoStuffBuffer, "") != 0)
      Serial.println(DoStuffBuffer);
    SerialMessage = 0;
  }
}

Which servo, and what is the current rating of the 5volt supply.
Leo..

Plenty of power @ 5A, servo linked above but al;so:

Are you sure that dual mode servo works like a regular one with the feedback pot? And did the pot that you disconnected have 3V3 across it or the servo power voltage?

I measured at 3.3v, and during some testing I was even powering the ESP32 3.3V Pin for quite a while with that output pin before realizing it had come unplugged.

I've had the hood up on a few servos and modified them for continuous rotation. I never measured the internal voltage across the pot but it's not surprising that it would be regulated to something different from the power supply.
Normally you would set the external pot (DAC in you case) so that you got no rotation when there is no PWM signal to the servo. Then the PWM signal just controls the speed and direction of the continuously rotating servo. Is that what happens in your setup?

Yes, that is 1 way I can do it, I also can set the PWM to the stop speed and then adjust the servo speed by changing the DAC value which is my preferred method.

Is this "stop speed" what would normally be 90º on the servo or is this some feature that the dual mode servo has?

Are you using the servo as a 0º - 180º servo or continuous rotation?

This servo really acts just like a normal servo. If I were to use one of the standard servo libraries, 1500us would be the stop speed, something like 750 is max reverse and 2250 for max forward. It can also be set for continuous using the programmer, but I'm currently not using that option.

Edit:
Sorry that is still confusing, yes 1500 would be the 90° position, 750 is max reverse position and 2250 max forward position.

So does a DAC setting of 3V3/2 mean no motion? With a PWM of 1500.

Yes, but only if the servo speed is also set to it's stop speed. If it is, then 2048 (+/- about 10) out of 4095 on DAC would be the stop speed of the servo.

Okay, I believe I understand what you are doing now. Out of curiosity, have you tried controlling speed and direction with PWM and just null the servo with the DAC? If so, did you have the same problem?

Yeah, actually since we've been discussing I was playing around with that option but the results are the same, too much jitter in one direction

I haven't examined your code yet to see if I see anything that would explain the problem but I will later. Do you have a servo that doesn't have the dual mode feature that you can test to see if it has the same problem?

I actually had worse issues with one that I had dissected quite awhile back. It was a while ago but I believe it was just too jittery in both directions at any speed. I will try again later this eve or...when i get a round tuit. I was using different code back then, so who knows.

Okay, that's a good plan. I haven't seen a problem like yours using a modified servo in what I would consider the "conventional" way.
Maybe someone else will chime in in the meanwhile.

Yes, understood it is a rare topic. There are some other servo modifications I have seen but I need the (AS5600 or whatever) outboard feedback since I am on a 7:1 gear ratio like the one I have down below in the reviews on THIS page, and need like 7 complete servo turns rather than the 300° this one provides.

I think I got it figured out. I was under the assumption the DAC made its own internal reference voltage - I thought I had read that somewhere or maybe I misunderstood. So I wasn't too concerned with that input voltage and never gave it a second thought.

Anyway, I found the use for that floating 3.3V coming from the old potentiometer terminal. I'm now using that to power the DAC VCC, instead of powering it from the ESP32.

Now both directions are working evenly and I notice no stuttering anywhere.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.