1

I have a fancy setup to cool my beer at a controlled temperature (you can find details and a photo here if you want). The input of my system is a temperature, the output is a servo position.

Long story short

On startup the servo is stable enough, but every time after 67s of runtime suddenly the servo starts vibrating. I have an oscilloscope and I can see that after 67s, instead of a steady 2.3 ms the length of the pulses oscillate in a small range, like between 2.300 and 2.320 ms, and it's enough of a range to be a problem.

What happens at 67s ?? Is there some kind of overflow ? I'm very puzzled because all in all my code for the pulse is very simple.

I'm having my problem on this code, branch bs18b20 of my repo. The pulse is generated line 293, with custom code.

More context

I had already asked for help right here with my setup. We had determined that using Servo.h along along with the 1Wire protocol make the interruptions used by both libraries get in the way of one another, so the servo receives bad values and vibrates too. In my last comment in this thread I said that the problem was solved, but I discovered later the problem of the servo getting unstable after some time, as exposed above.

To overcome that I changed tactics, switched my bs18b20 for a lm35 which doesn't need 1Wire, and switched back to Servo.h. It worked very well... in my electronics lab. But then (reality sucks) I found out when actually using my device in the kitchen that sometimes the temperatures start getting very wrong, and I suspect that the humidity alters the voltage reading of my lm35. And that's why I'm trying to change back again to my previous solution with a waterproof bs18b20.

While writing this, stackexchange proposed this thread with a very promising solution, I will definitely try that. Nonetheless, for the sake of it, I'm still interested in knowing what causes my problem above, so I'm posting this anyway.

8
  • please add the code to the question
    – jsotola
    Commented Apr 23, 2022 at 17:45
  • try putting timer_20 = now; at the begining of the if block
    – jsotola
    Commented Apr 23, 2022 at 17:49
  • decimal 67,000,000 microseconds is hexadecimal 3FE56C0 ... that is very close to hex 4000000 ... it may be some rollover, but it makes no sense because that is a 27 bit number
    – jsotola
    Commented Apr 23, 2022 at 18:06
  • Do you have the same issue if you use delayMicroseconds() instead of the while (micros() < time_micros) loop? Commented Apr 23, 2022 at 18:23
  • Yes @jsotola micros() overflows after about 70 minutes, and millis() after 70 000 minutes I guess. So that shouldn't be my problem. I'll try moving the affectation of timer_20, you're right, but I don't see how that may help. Commented Apr 23, 2022 at 19:05

1 Answer 1

1

Without looking into the details of your code, it looks like you are having issues with interrupts. According to your comment, this simple code gives a very uneven pulse length:

digitalWrite(SERVO_PIN, HIGH);
delayMicroseconds(servo_value*1000);
digitalWrite(SERVO_PIN, LOW);

The function delayMicroseconds() delays for the requested time, plus any time taken by interrupts. Thus, if the delay is inconsistent while servo_value does not change, it can only be because of interrupts. Trying to understand the exact symptoms is likely to be quite hard, as one would have to dig into the source of the libraries you are using, and look at every ISR in there. The fact that the version delaying with micros() only starts misbehaving after some time is probably due to some very slow “beating” between the timing of your servo and the timing of an interrupt. The fact that the pulse is occasionally 20 µs longer is consistent with it having been interrupted for 20 µs, which for an ISR would be kind of long but not insanely long.

Rather than trying to analyze in detail how the interrupts are interfering with your servo, I would recommend sidestepping the problem completely and driving the servo with a hardware-generated PWM signal. That would give a very consistent pulse length, completely immune to being disrupted by interrupts. You can achieve 0.5 µs resolution with a 20 ms period by setting Timer 1 to fast PWM and clocking it at 2 MHz, like this:

// Configure timer 1 for driving a servo
// on pin OC1A = PB1 = digital 9.
void setup_timer()
{
    TCCR1A = 0;           // undo the Arduino core configuration
    TCCR1B = 0;           // ditto
    ICR1   = 40000 - 1;   // period = 20 ms = 40000 timer ticks
    OCR1A  =  3000 - 1;   // pulse length = 1.5 ms = 3000 timer ticks
    TCCR1A = _BV(COM1A1)  // non-inverting PWM on OC1A
           | _BV(WGM11);  // mode 14: fast PWM, TOP = ICR1
    TCCR1B = _BV(WGM12)   // ditto
           | _BV(WGM13)   // ditto
           | _BV(CS11);   // clock @ F_CPU/8 = 2 MHz
}

Then you can change the pulse length simply by writing to OCR1A: write one less than the desired pulse length, in units of 0.5 µs.

1
  • You're very probably right, and your solution is the one I've adopted, though I like that some nices folks did the ugly part, I used this : learn.adafruit.com/neopixels-and-servos/the-ticoservo-library and it works like a charm. It does under the hood exactly what you propose, the article is great. Still, I would have liked to understand the threshold of 67 (more like 66 actually) seconds. Commented Apr 23, 2022 at 21:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.