Why? Arduino Servo. Pot. LED - Page 1
Forum section: electronics / general

author: metrologist
author_rank: Super Contributor
timestamp: February 10, 2019, 03:27:43 pm
content: Arduino sets the servo position via a pot. The LED lights when the servo function moves the servo to a new position.When I physically twist the servo, the LED comes on and I don't know why?My loop just calls these two functions. The first reads a pot pin 5 times and maps an average to degrees. The second moves the servo.Code:void pot3(){pot = 0;for (int i=1; i <= 5; i++){pot = pot + analogRead(potpin);}pot = map(pot/5, 0, 1023, 0, 180);}void servo1x(int pos, int dlyx) {if (pos < pos1){digitalWrite(13, HIGH); // turn the LED on (HIGH is the voltage level)for(int i = pos1; i >= pos && i >= servo1start; i--) // goes from degrees to degrees{servo1.write(i); // tell servo to go to position in variable 'pos'delay(dlyx); // waits ms for the servo to reach the positionpos1 = i;}digitalWrite(13, LOW); // turn the LED off (LOW is the voltage level)}else if (pos > pos1) {digitalWrite(13, HIGH); // turn the LED on (HIGH is the voltage level)for(int i = pos1; i <= pos && i <= servo1stop; i++) // goes from degrees to degrees{ // in steps of 1 degreeservo1.write(i); // tell servo to go to position in variable 'pos'delay(dlyx); // waits ms for the servo to reach the positionpos1 = i;}digitalWrite(13, LOW); // turn the LED off (LOW is the voltage level)}}Just because I apply pressure to the servo there is no way Arduino can know about it, so why does LED come on? It is very consistent with pressure too. It goes from a flicker to strong brightness with more pressure.Also, at the endpoints of my pot, the servo is not stable and twitches constantly.Finally, I have an LCD function that will print the current angle. If I include a call to that function, none of the above happens and the servo takes consistent coarse steps to any new position.it's more complex than it needs to be and works fine, but maybe takes a bit of time...?Code:void lcd2(){// picture loopu8g.firstPage();do {draw2();}while( u8g.nextPage() );xPos = xPos + 14;if (xPos >= 128) xPos = 0;if (++yPos >= 76) yPos = 0;}void draw2() {//graphic commands to redraw the complete screen should be placed here//u8g.setFontPosTop();//u8g.setFont(u8g_font_unifontr);u8g.setFont(u8g_font_fub11); //fub11, 14, 17, 20, 25u8g.setPrintPos(0, 12);u8g.print(pot);u8g.print("deg ");u8g.drawStr(xPos, 62, "."); //scrolling marq2uee}

author: Simon
author_rank: Unknown
timestamp: February 10, 2019, 03:38:02 pm
content: Does the servo not have any feedback ? like a built in pot that the arduino reads to see it's position? or is the servo a self contained closed loop one?

author: metrologist
author_rank: Super Contributor
timestamp: February 10, 2019, 03:43:07 pm
content: It is a regular servo, with PWM input pin and two power pins. Arduino just sets PWM on a pin and there is no read function. The servo has a pot to know where it is and some circuit to read that and move the motor to the PWM position.I take it back, if I include LCD function. I just have to twist servo a lot. AND, the degrees change on my LCD as I fight the servo...

author: ebastler
author_rank: Super Contributor
timestamp: February 10, 2019, 03:57:29 pm
content: The servo has aninternalfeedback loop, of course. So as you (try to) turn it manually, it will sense that and drive its motor, to try and stay at the position which the PWM signal from the Arduino requests. I'm wondering whether you might be dealing with a hardware issue here? Spikes from the motor current messing with the local ground level, and hence causing the LED to flicker?

author: metrologist
author_rank: Super Contributor
timestamp: February 10, 2019, 04:01:54 pm
content: Oh, I think it's simple. The servo is powered from Arduino and loads the supply, changes the ADC reference and thinks the pot moved.

author: ebastler
author_rank: Super Contributor
timestamp: February 10, 2019, 04:09:15 pm
content: Quote from: metrologist on February 10, 2019, 04:01:54 pmAre you saying you just realized this, or knew it all along and posted this as a little quiz?

author: Simon
author_rank: Unknown
timestamp: February 10, 2019, 04:10:35 pm
content: do you have a multimeter or better scope to verify. If you force the servo you will effectively make it's motor work at full torque / stall current and yes the common power supply could go all over the place. But the pot is on the same supply is it not? but granted the timing of the program could cause such a thing or even a crash and reboot of the chip.

author: sokoloff
author_rank: Super Contributor
timestamp: February 10, 2019, 04:37:20 pm
content: Couple of code tips that might be relevant.You can use the map function to do the smoothing rather than doing the division yourself. Doing it this way means only one integer truncation rather than two, leading to generally smoother results.Code:void pot3() {pot = 0;for (int i=1; i <= 5; i++){pot = pot + analogRead(potpin);}pot = map(pot, 0, 1023*5, 0, 180);}I presume pos1 is a global variable. If it's only used in servo1x, you could make it a static variable in that function.You are also commanding the servo to its (presumed) existing position as the first step in every loop.To fix that and to reduce code duplication (which tends to allow bugs to hide), I'd cleanup the servo1x function as follows:Code:void servo1x(int pos, int dlyx) {if (pos < servo1start) // These two ifs may prevent the flickeringpos = servo1start; // at the pot extremes, if the issue isif (pos > servo1stop) // that you were commanding to a positionpos = servo1stop;// outside the servo1 limits.if (pos == pos1) // Nothing to doreturn;int step = (pos < pos1) ? -1 : 1; // determine which direction to godigitalWrite(13, HIGH); // turn the LED on (HIGH is the voltage level)for ( bool skipfirst = true; pos1 >= servo1start && pos1 <= servo1stop; pos1 += step) // goes from degrees to degrees{if (skipfirst) {skipfirst = false; // Skip first position command and associated delaycontinue;}servo1.write(pos1); // tell servo to go towards target position in variable 'pos'delay(dlyx); // waits ms for the servo to reach the positionif (pos1 == pos) // when reaching target position, break out of the loopbreak;}digitalWrite(13, LOW); // turn the LED off (LOW is the voltage level)}Note that if you do this, you will need to ensure there is an initialization of pos1 that causes the variable to be within the range [servo1start, servo1stop] and puts the servo there.

author: sokoloff
author_rank: Super Contributor
timestamp: February 10, 2019, 04:47:14 pm
content: You can handle initialization in one of two ways:Code:void servo1x(int pos, int dlyx) {if (pos1 < servo1start) // These two ifs allow for self-initializationpos1 = servo1start;if (pos1 > servo1stop)pos1 = servo1stop;if (pos < servo1start) // These two ifs may prevent the flickeringpos = servo1start; // at the pot extremes, if the issue isif (pos > servo1stop) // that you were commanding to a positionpos = servo1stop;// outside the servo1 limits.if (pos == pos1) // Nothing to doreturn;int step = (pos < pos1) ? -1 : 1; // determine which direction to godigitalWrite(13, HIGH); // turn the LED on (HIGH is the voltage level)for ( bool skipfirst = true; pos1 >= servo1start && pos1 <= servo1stop; pos1 += step) // goes from degrees to degrees{if (skipfirst) {skipfirst = false; // Skip first position command and associated delaycontinue;}servo1.write(pos1); // tell servo to go towards target position in variable 'pos'delay(dlyx); // waits ms for the servo to reach the positionif (pos1 == pos) // when reaching target position, break out of the loopbreak;}digitalWrite(13, LOW); // turn the LED off (LOW is the voltage level)}and make a call to servo1x(90, dlyx) or servo1x(180, dlyx), servo1x(0, dlyx) to initialize the servo to a "known" position prior to having it follow the pot.Alternately, you could write a servo init function like this and call it once before looping.Code:void servo1x_init(int dlyx) {if (pos1 < servo1start) // These two ifs ensure pos1 is initialized in rangepos1 = servo1start;if (pos1 > servo1stop)pos1 = servo1stop;servo1x(180, dlyx);// and command the servo to one extremeservo1x(90, dlyx);// then the middle}

author: sokoloff
author_rank: Super Contributor
timestamp: February 10, 2019, 04:49:04 pm
content: (If you do the second method to initialize, you don't need the two pos1 ifs in the servo1x function and can use the one from my first post.)Note that my code is not only not tested, I didn't even check to see if it compiles. I looked it over visually and simulated it in my head, but like everything that comes out of my fingers, it's subject to errors.

author: sokoloff
author_rank: Super Contributor
timestamp: February 10, 2019, 04:57:02 pm
content: You can also use the Arduino macro constrain for improved readability:Replace the first two ifs in code sample 3 withCode:pos1 = constrain(pos1, servo1start, servo1stop);And the two "pos" ifs in all code samples with:Code:pos = constrain(pos , servo1start, servo1stop);

author: metrologist
author_rank: Super Contributor
timestamp: February 10, 2019, 06:30:05 pm
content: I don't see where I am commanding the servo in every loop as the condition has to be greater or less than, so if the pot does not change, then both of the if conditions should be bypassed and the function exits.But I do have a problem with correlating position degrees and dac value, so I changes the function.Code:void pot3(){for (int i=1; i <= 5; i++){pot = pot + map(analogRead(potpin), 0, 1023, 0, 180);}pot = pot/6;}Now the endpoints only flicker the LED when the pot is beyond the limit setting, which are are due to not every servo can actually move to 0 and 180 degrees without binding.

author: metrologist
author_rank: Super Contributor
timestamp: February 10, 2019, 06:31:58 pm
content: Quote from: ebastler on February 10, 2019, 04:09:15 pmI think there is no good answer to this kind of question, but I wouldn't attempt to quiz the forum.

author: ebastler
author_rank: Super Contributor
timestamp: February 10, 2019, 07:53:17 pm
content: Quote from: metrologist on February 10, 2019, 06:31:58 pmHmm, I indeed think there would have been a "good answer".You asked a question to the forum, I suggested an answer, you (instead of saying "thanks") responded "Oh, I think it's simple". Which makes me wonder whether you were feeling the need to explain something tome, namely the solution to a little quiz you had devised.The "good answer" would have been: "No, I was really at a loss, but this may indeed be the root cause. Thank you."

author: sokoloff
author_rank: Super Contributor
timestamp: February 10, 2019, 07:55:37 pm
content: Quote from: metrologist on February 10, 2019, 06:30:05 pmWhat I mean is that, when you do have a command to issue, you always issue the current position .Say pos1 = 10 and pos = 15.Your original code commands 10, 11, 12, 13, 14, 15, with a delay at each comma position.Presumably, you could command 11, 12, 13, 14, 15, with a delay at each comma.Quote from: metrologist on February 10, 2019, 06:30:05 pmHere, since you're not initializing pot to 0 before accumulating, you're mixing in the effect of a low pass filter (because you're adding 5 times the new measure and then dividing by 6) and thereby smoothing (taking 1 part the old pot angle and 5 parts the new).If you want it work that way, this code is probably clearer and more cleanly tweakable by changing the DEFINE'd values:Code:#define NUM_ADC_READS 5// Higher Smoothing factors are, well, smoother. Range is 0 to 127#define SMOOTHING_FACTOR 21void pot3() {int newPot = 0;for (int i=1; i <= NUM_ADC_READS; i++){newPot = newPot + analogRead(potpin);}newPot = map(newPot, 0, 1023*NUM_ADC_READS, 0, 180);// Now smooth the pot reading by mixing with the old reading.pot = (SMOOTHING_FACTOR*pot + (128-SMOOTHING_FACTOR)*newPot) / 128;}It also avoids doing a division and associated truncation (in the map function) so many times.21 is approximately the same amount of low pass filtering that your code immediately above does.

author: sokoloff
author_rank: Super Contributor
timestamp: February 10, 2019, 10:33:29 pm
content: Another approach, designed to keep the latency through the loop() function constant is to move a maximum of a fixed number of steps in the servo positioning function:Code://#define USE_HYSTERESIS#define HYSTERESIS_X 1#define MAX_STEP_X 1void servo1x(int pos) {#ifdef USE_HYSTERESISif (constrain(pos, pos1-HYSTERESIS_X, pos1+HYSTERESIS_X) == pos) {digitalWrite(13, LOW);return;}#endifpos = constrain(pos, servo1start, servo1stop); // Constrain to servo limitsif (pos == pos1) { // Nothing to do but turn off the LEDdigitalWrite(13, LOW);return;}pos1 = constrain(pos, pos1-MAX_STEP_X, pos1+MAX_STEP_X); // Constrain to max of MAX_STEP_X away from current position.digitalWrite(13, HIGH); // turn the LED on (HIGH is the voltage level)servo1.write(pos1); // tell servo to move towards target position passed in variable 'pos'}And move the delay into the loop function:Code:void loop() {// Do whatever you're doing now// Read the pot// Move Servo Xservo1x(pot);// Update LCD// Whatever else// Add a delay *only* once in loop:delay(dlyx);}This makes the system more responsive when the servo has a long commanded move. It also allows "diagonal movement" for a system with multiple servos. (Basically, the X motion happens continuously but only one "step" at a time each time through the loop and the LCD keeps updating, the pot is still being read, if there are other servos, they can also be moving at the same time, etc.)The hysteresis processing code is designed to reject a small amount of noise in the pot measurement (at the expense of final accuracy). It's optional and not compiled in in the sample code above.

author: metrologist
author_rank: Super Contributor
timestamp: February 11, 2019, 07:21:19 am
content: Quote from: ebastler on February 10, 2019, 07:53:17 pmOh, sorry. I don't seem to be much good at anything. It was very much conversational and unfolding as we spoke, so to say... OK, yes, I just realized it as I posted, or as you posted. I guess an engineer would have known as readily as one would know to put on their pants.Spikes on the ground line was not what I was thinking, but thanks. That might actually be what is going on rather than a weak sagging regulator, but I don't know. I was just perplexed there for a sec as I twisted the servo and the dac reading on the LCD responded proportionately along with the LED.

author: metrologist
author_rank: Super Contributor
timestamp: February 11, 2019, 07:49:38 am
content: Quote from: sokoloff on February 10, 2019, 10:33:29 pmOK, that's interesting and I'll have to digest it and see how it works. I know my coding style is still very simpleton. This code was pieced together as a quick esc test. The servo function had a delay to control the servo speed. It was for a robot that all worked in tandem, but the function is blocking so nothing else works well with it.I've been trying to think of the micro as spinning very fast and looking for a way to trigger shorter commands, such as every second check thermistor, every n seconds move servo one step, only write the part of the LCD when it changes, and put a timer on the marching time dot so it works with a RTC, etc. It seems you could have the effect of multitasking.I ordered 20 servos for another 4 leg robot project so there will be time to explore this soon.

author: sokoloff
author_rank: Super Contributor
timestamp: February 11, 2019, 01:00:44 pm
content: Quote from: metrologist on February 11, 2019, 07:49:38 am"Simple and working" or "simple and making progress" beats "complicated and not working" or "never written" every day of the week, so don't sweat it a bit.Sorry for so many followup posts above, but I first focused on the micro problem (no pun intended) and only later thought "hey, if he's calling it 'servo-x', that probably means there's another servo in the mix and he probably doesn't want blocking movement"

author: metrologist
author_rank: Super Contributor
timestamp: February 11, 2019, 03:26:13 pm
content: Well, your thought pretty much is spot on...I called the function servo1x because servo1 caused an error that took me forever to discover. I already declared servo1 as the servo object.This was actually a two-servo robot and the servos worked in sequence rather than tandem like I was thinking.