Weird problem with code trying to control a couple of digital servos

Hi all,

I'll try to keep this short and sweet. I'm trying to use a DFRobot Beetle board to control two digital servos in a combat robot. The beetle board is to take the signal from the receiver, extend it so I get the full 180 degree throws from the servos and reverse one of the throw directions so the servos act in unison.

The code below was created to eliminate some noticable 'ticking' in the operation of the servos, which it pretty much has done. There's only some minor ticking at centre stick now but that's not a problem. The issue I have with the code below is the extended throw mapping that's represented by the '500' and '2500' figures below. With the code as it is below, not only do the servos not work but the serial printing doesn't work either. If I remove the extended throw mapping and revert these figures back to 1000 and 2000 where required, it all works fine. Therefore how do I rectify the code so that the extended throw mapping I'm after can function? It feels like there's some kind of clash somewhere.

Cheers

Dan

int pwmInput = A0;
int channel1 = 9;
int channel2 = 10;
int pwmValue;
int servo1Value;
int servo2Value;

void setup() {
pinMode(channel1, OUTPUT);
pinMode(channel2, OUTPUT);
Serial.begin(9600);
}

void loop() {

pwmValue = pulseIn(pwmInput, HIGH, 25000); // Read PWM signal
Serial.print("pwm input Value: ");
Serial.println(pwmInput);

servo1Value = map(pwmValue, 1000, 2000, 500, 2500);
Serial.print("Servo 1 Value: ");
Serial.println(servo1Value);

servo2Value = map(pwmValue, 1000, 2000, 2500, 500);
Serial.print("Servo 2 Value: ");
Serial.println(servo2Value);

servoPulse(servo1Value, channel1);
servoPulse(servo2Value, channel2);


}
void servoPulse(long pulseLength, int pin) {
long start = micros();
digitalWrite(pin, HIGH);
while ((micros() - start) < pulseLength) { ; }
digitalWrite(pin, LOW);
}

That function is going to make everything else in your code unresponsive. Why don't you use the Servo library?

2 Likes

This is a blocking delay, and the MCU won't do anything else while stuck in that loop.

1 Like

My servos were incredibly twitchy when i tried using a nice and simple code with a servo library and the piece of code you've both pointed out got rid of the twitching but has obivously caused some other problems.

The original twitching was more like a ticking at a regular interval so I reckon the digital servos I'm using were working faster that the arduino if that's possible?!

Which servo library was 'twichy', please, and what code was being used to test - the examples that come with that library, for example?

So maybe you should look for the actual cause instead of hamstringing the rest of your code. The solution you've come to will prevent you from doing anything else with this code. You've only created larger problems.

Something like this...

#include <Servo.h>

Servo servoOutput1;
Servo servoOutput2;

int inputSignal;
int outputSignal1;
int outputSignal2;

void setup() {
  servoOutput1.attach(9);  // Connect servoOutput1 to pin 9
  servoOutput2.attach(10); // Connect servoOutput2 to pin 10
}

void loop() {
  inputSignal = pulseIn(2, HIGH); // Read input signal from radio-controlled receiver on pin 2

  // Map the input signal to the range of 0-180 degrees for servo movement
  outputSignal1 = map(inputSignal, 1000, 2000, 0, 180);
  outputSignal2 = 180 - outputSignal1; // Reverse one of the output signals

  servoOutput1.write(outputSignal1); // Send output signal 1 to servoOutput1
  servoOutput2.write(outputSignal2); // Send output signal 2 to servoOutput2

  delay(15); // Delay for stability
}

Being inexperienced, That's why I have come to the forum to look for some advice on either a fix to the current code or guidance towards something totally different that may work. The servos work fine when used with RC gear so there's no problems on that front.

delay(15); // Delay for stability

Does nothing useful, and prevents the MCU from reading the RC receiver.

I would verify the stability of pulse in as a first step. Print a thousand readings, or store max and min for a thousand readings. You can't get servo stability if that pulseIn value changes at all. You may need to apply filtering to that signal to achieve anything useful.

And yes, delay(xx) does nothing to help you. The servo update interrupt routine in Servo.h runs at 50 Hz. You can write to the servo once per second, or once per ms, the servo update happens at the same rate.

I'm sure I didn't understand. If the servos work with the signal that usually does, why are you trying to make it work with extended throwing of anything?

The IDE servo example should let you determine the min and max values that make sense for your servo, send it nothing outside that range.

And +1 for losing the hand-made servo pulses. Many thousands of ppl knowing no more or less than you find the servo library to be entirely the path of least resistance.

Note: map() will happily extrapolate, if you need to, consider using the constrain() function, its frequent companion, to force the servo values into the usable range.

I interpret your other observations to mean you are providing adequate power. Tell us what is powering the servos and how does that power get from the source to the servo exactly?

a7

I reiterate. This, as a source of a value to write to a servo, is not a valid test of stability. It could be all over the map in pulse width, and your resultant shaming of the servo code for instability is way, way off base.
But, your silence on this topic makes me think you've moved on, or are only available occasionally, so we'll wait for your return now.

These are the servos. They are directly powered from a 3s lipo. A BEC circuit running from the same battery powers the receiver and Arduino. I'm 99% sure they can actually do the 180 degree movement but I'll use the sweep example to check.

https://www.aliexpress.com/item/1005004600407461.html?isdl=y&aff_fsk=_oBXVPn9&src=Connexity2023GBchannel&aff_platform=aff_feeds&aff_short_key=_oBXVPn9&pdp_npi=4%40dis!GBP!36.23!32.63!!!!!%40!12000029783796553!afff!!!&isdl=y&aff_fsk=_oBXVPn9&src=Connexity2023GBchannel&aff_platform=aff_feeds&aff_short_key=_oBXVPn9&dp=17192610418610002525714121148008005&cn=326576&cv=90dc9d53ad3a459934a2d8241df6ed58

In your opinion then, what's the best way to utilise the signal generated by a RC receiver so it can be manipulated by an Arduino board?

Okay, I'll bite. Don't use it point-blank. You must condition it. The reason is, the pulse-width measurement you're doing is quite likely returning numbers like(pulled out of a hat)
270, 275, 274, 278, etc. because of

  • v ariability of the source device
  • variability of the timing of the measuring routine
  • interrupts happening on the Arduino(like, for example, the millis() timer), messing with the pulse measurement

So I ask again, have you actually characterised the numbers you're getting, by printing, binning, or otherwise analysing them? If not, you're accepting a pig in a poke.

By the way, what is this "RC Receiver", and how does it arrive at a pulse output? A link to a device on the web might illuminate.
I'm suspecting some form of oscillating circuit who's output may have significant jitter, which, when coupled with the known limitations of the Arduino pulse measurement may result in pulse widths that vary enough to produce servo jitter - but that may be off base, I have no data.

Looking at the device and it's application space, I'd expect it's not the primary source of your problem, though I'd still like to see the PWM outputs on a scope for verification. However, that still leaves the Arduino's reading of the pulse width as a possible source of jitter.

A further question. Why is it we're connecting an Arduino between a PWM source and a Servo? Is the PWM source(i.e. the DSMP output) not suitable for the Servo directly?

That device should provide N channels of PWM output of exactly the frequency and duty cycle that a common hobby servo accepts.

In the old days, servos were connected directly to the receiver, and the controls at the TX would be directly translated to servo positions at the receiving end.

You could try connecting a servo directly, always being sure to provide the servo with adequate power and obvsly check the receiver for voltages it can work with.

Presumably so that the incoming control settings could be processed in some way, like with a curve away from linear, or reversing direction or mixing, say, of two or three control signals.

These days there's a lunar lander * unknown_factor worth of processing power in many r/c toys.

a7