user71612: Unnecessary context but hey I'm guessing you might be interested
Hi, I want to build a PID temperature regulator with a servo that presses on a silicon pipe to adjust the flux of liquid passing through. The liquid is hot beer that must be cooled very quickly from 100°C to 25°C, and it passes through a heat exchanger where also passes in the other direction a constant flux of cold water. The lesser the flux of beer, the colder it becomes. But it is cumbersome to regulate it by hand and I'd like to automate it, because I (think I) can.
Overview of the system
My system is mainly composed of an Arduino Uno, a servo motor (PWM on pin 11), a BS18B20 1Wire temperature probe (on pin 2), and a few buttons. In 'manual' mode I can choose the position of the servo with the buttons and it is very stable.
I also have a buzzer on pin 13 and will add an I2C screen when the core works.
My problem
As soon as I start to request the temperature at regular intervals (like every 200 ms), the servo flickers all the time, which is boring, looks bad, and probably will reduce its life expectancy. I think every time I request a temperature something happens on the PWM, like a brief reset or something, and the servo reacts and then gets back to its original position.
The only power I've tested so far is the USB cable from the computer. In case you wonder, the code is here:
#include <AceButton.h> // https://github.com/bxparks/AceButton
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Servo.h>

Servo myservo; // create servo object to control a servo
// twelve servo objects can be created on most boards

#define ONE_WIRE_BUS 2 // pin for thermometer

using namespace ace_button;

// The pin number attached to the button.
const int BUTTON_SET_PIN = 12;
const int BUTTON_MORE_PIN = 4;
const int BUTTON_LESS_PIN = 0;

const int BUTTON_MODE2_PIN = 5;
const int BUTTON_MODE3_PIN = 7;

const int LED_PIN = LED_BUILTIN;
const int SERVO_PIN = 11;

const int MODE_THERMOMETER = 1;
const int MODE_REGULATOR = 2;
const int MODE_MANUAL = 3;
int mode;

// LED states. Some microcontrollers wire their built-in LED the reverse.
const int LED_ON = HIGH;
const int LED_OFF = LOW;

// Both buttons automatically use the default System ButtonConfig. The
// alternative is to call the AceButton::init() method in setup() below.
AceButton buttonSet(BUTTON_SET_PIN);
AceButton buttonMore(BUTTON_MORE_PIN);
AceButton buttonLess(BUTTON_LESS_PIN);

ButtonConfig modeConfig;
AceButton buttonMode2(&modeConfig);
AceButton buttonMode3(&modeConfig);

// Setup a oneWire instance to communicate with any OneWire devices
// (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

// variables for tune
float value = 50;
unsigned long tuneStart;
float max = 175;
float min = 10;

void handleModeEvent(AceButton *, uint8_t, uint8_t);

void handleModeEvent(AceButton *button, uint8_t eventType, uint8_t /* buttonState */) {
  switch (eventType) {
  case AceButton::kEventPressed:
    if (button->getPin() == BUTTON_MODE2_PIN) {
      mode = MODE_REGULATOR;
      printMode();
    }

    if (button->getPin() == BUTTON_MODE3_PIN) {
      mode = MODE_MANUAL;
      printMode();
    }
    break;
  case AceButton::kEventReleased:
    if (button->getPin() == BUTTON_MODE2_PIN) {
      mode = MODE_THERMOMETER;
      printMode();
    }
    if (button->getPin() == BUTTON_MODE3_PIN) {
      mode = MODE_REGULATOR;
      printMode();
    }
    break;
  }
}

void printMode()
{
  Serial.print("Mode ");
  Serial.println(mode);
}

void handleButtonEvent(AceButton *, uint8_t, uint8_t);

void setup() {
  Serial.begin(9600);
  while (!Serial); // Wait until Serial is ready - Leonardo/Micro
  Serial.println(F("Test 3 boutons - la suite en 115200 bps"));
  delay(300);

  // servo
  myservo.attach(11);

  // thermal probe
  sensors.begin();
  sensors.setWaitForConversion(false);
  sensors.setResolution(10);

  Serial.begin(115200);
  while (!Serial); // Wait until Serial is ready - Leonardo/Micro
  Serial.println(F("setup(): begin"));

  // Initialize built-in LED as an output.
  pinMode(LED_PIN, OUTPUT);
  pinMode(SERVO_PIN, OUTPUT);

  // Buttons use the built-in pull up register.
  pinMode(BUTTON_SET_PIN, INPUT_PULLUP);
  pinMode(BUTTON_MORE_PIN, INPUT_PULLUP);
  pinMode(BUTTON_LESS_PIN, INPUT_PULLUP);

  pinMode(BUTTON_MODE2_PIN, INPUT_PULLUP);
  pinMode(BUTTON_MODE3_PIN, INPUT_PULLUP);

  buttonMode2.init(BUTTON_MODE2_PIN);
  buttonMode3.init(BUTTON_MODE3_PIN);

  // Configure the ButtonConfig with the event handler, and enable all higher
  // level events.
  ButtonConfig *buttonConfig = ButtonConfig::getSystemButtonConfig();
  buttonConfig->setEventHandler(handleButtonEvent);
  buttonConfig->setFeature(ButtonConfig::kFeatureClick);
  buttonConfig->setFeature(ButtonConfig::kFeatureLongPress);
  buttonConfig->setFeature(ButtonConfig::kFeatureRepeatPress);

  // Configs for the tune-up and tune-down buttons. Need RepeatPress instead of
  // LongPress.
  modeConfig.setEventHandler(handleModeEvent);
  modeConfig.setFeature(ButtonConfig::kFeatureClick);
  // These suppressions not really necessary but cleaner.
  modeConfig.setFeature(ButtonConfig::kFeatureSuppressAfterClick);

  // Check if the button was pressed while booting
  if (buttonMode3.isPressedRaw()) {
    mode = MODE_MANUAL;
    printMode();
  } else if (buttonMode2.isPressedRaw()) {
    mode = MODE_THERMOMETER;
    printMode();
  } else {
    mode = MODE_REGULATOR;
    printMode();
  }
  Serial.println(F("setup(): ready"));
}

long timer_200 = 0;
long timer_1000 = 0;

void loop() {
  // Should be called every 4-5ms or faster, for the default debouncing time of ~20ms.
  buttonSet.check();
  buttonMore.check();
  buttonLess.check();
  buttonMode2.check();
  buttonMode3.check();

  // every 200 ms
  long now = millis();
  if (now > timer_200 + 200) {
    timer_200 = now; // to reinit timer
    if (mode == MODE_MANUAL) {
      myservo.write(value);
    }
  }

  // every 1s
  if (now > timer_1000 + 1000) {
    timer_1000 = now; // to reinit timer
    // get temperature requested last time
    Serial.print("Temperature for Device is: ");
    Serial.println(sensors.getTempCByIndex(0));
    // and request a new one
    sensors.requestTemperatures(); // Send the command to get temperatures
    if (mode == MODE_THERMOMETER) {}
  }
}

// The event handler for both buttons.
void handleButtonEvent(AceButton *button, uint8_t eventType, uint8_t buttonState) {
  switch (eventType) {

  case AceButton::kEventClicked:
    if (button->getPin() == BUTTON_MORE_PIN) {
      value = value * 1.01;
      if (value > max) {
        value = max;
      }

      Serial.println(value);
    }

    if (button->getPin() == BUTTON_LESS_PIN) {
      value = value * .99;
      if (value < min) {
        value = min;
      }
      Serial.println(value);
    }

    if (button->getPin() == BUTTON_SET_PIN) {
      Serial.println(F("set"));
    }
    break;

  case AceButton::kEventLongPressed:
    if (button->getPin() == BUTTON_SET_PIN) {
      Serial.println(F("set long press"));
    }

    if (button->getPin() == BUTTON_MORE_PIN || button->getPin() == BUTTON_LESS_PIN) {
      tuneStart = millis();
    }
    break;
  case AceButton::kEventRepeatPressed:

    if (button->getPin() == BUTTON_MORE_PIN) {
      tuneUp();
    }

    if (button->getPin() == BUTTON_LESS_PIN) {
      tuneDown();
    }
    break;
  }
}

void tuneUp() {
  unsigned long duration = millis() - tuneStart;
  float ratio = 1 + (0.00001f * 4 * duration);
  value = value * ratio;
  if (value > max) {
    value = max;
  }
  Serial.println(value);
}

void tuneDown() {
  unsigned long duration = millis() - tuneStart;
  float ratio = 1 - (0.00001f * 4 * duration);
  value = value * ratio;
  if (value < min) {
    value = min;
  }
  Serial.println(value);
}

It's still quite a prototype.
What could cause this and how can I prevent it?

user61722: I suspect the 1Wire libary disables (and enables) interrupts when it sees fit. This will cause servo "hickups" because the servo library uses interrupts.
If I'm right then your best bet is using a servo library that doesn't use interrupts. I always use a hardware PWM library for driving servos, so I have no servo library recommendations to make, but maybe somebody else has.
Also: you are doing a Serial.begin() twice, and the variables you use for timing (using millis()) should be unsigned long, not long.

71612: <p><strong>Unnecessary context but hey I'm guessing you might be interested</strong></p>
<p>Hi, I want to build a PID temperature regulator with a servo that presses on a silicon pipe to adjust the flux of liquid passing through. The liquid is hot beer that must be cooled very quickly from 100°C to 25°C, and it passes through a heat exchanger where also passes in the other direction a constant flux of cold water. The lesser the flux of beer, the colder it becomes. But it is cumbersome to regulate it by hand and I'd like to automate it, because I (think I) can.</p>
<p><strong>Overview of the system</strong></p>
<p>My system is mainly composed of an Arduino Uno, a servo motor (PWM on pin 11), a BS18B20 1Wire temperature probe (on pin 2), and a few buttons. In 'manual' mode I can choose the position of the servo with the buttons and it is very stable.</p>
<p>I also have a buzzer on pin 13 and will add an I2C screen when the core works.</p>
<p><strong>My problem</strong></p>
<p>As soon as I start to request the temperature at regular intervals (like every 200 ms), the servo flickers all the time, which is boring, looks bad, and probably will reduce its life expectancy. I think every time I request a temperature something happens on the PWM, like a brief reset or something, and the servo reacts and then gets back to its original position.</p>
<p>The only power I've tested so far is the USB cable from the computer. In case you wonder, the code is here:</p>
<pre class="lang-cpp prettyprint-override"><code>#include &lt;AceButton.h&gt; // https://github.com/bxparks/AceButton
#include &lt;OneWire.h&gt;
#include &lt;DallasTemperature.h&gt;
#include &lt;Servo.h&gt;

Servo myservo; // create servo object to control a servo
// twelve servo objects can be created on most boards

#define ONE_WIRE_BUS 2 // pin for thermometer

using namespace ace_button;

// The pin number attached to the button.
const int BUTTON_SET_PIN = 12;
const int BUTTON_MORE_PIN = 4;
const int BUTTON_LESS_PIN = 0;

const int BUTTON_MODE2_PIN = 5;
const int BUTTON_MODE3_PIN = 7;

const int LED_PIN = LED_BUILTIN;
const int SERVO_PIN = 11;

const int MODE_THERMOMETER = 1;
const int MODE_REGULATOR = 2;
const int MODE_MANUAL = 3;
int mode;

// LED states. Some microcontrollers wire their built-in LED the reverse.
const int LED_ON = HIGH;
const int LED_OFF = LOW;

// Both buttons automatically use the default System ButtonConfig. The
// alternative is to call the AceButton::init() method in setup() below.
AceButton buttonSet(BUTTON_SET_PIN);
AceButton buttonMore(BUTTON_MORE_PIN);
AceButton buttonLess(BUTTON_LESS_PIN);

ButtonConfig modeConfig;
AceButton buttonMode2(&amp;modeConfig);
AceButton buttonMode3(&amp;modeConfig);

// Setup a oneWire instance to communicate with any OneWire devices
// (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&amp;oneWire);

// variables for tune
float value = 50;
unsigned long tuneStart;
float max = 175;
float min = 10;

void handleModeEvent(AceButton *, uint8_t, uint8_t);

void handleModeEvent(AceButton *button, uint8_t eventType, uint8_t /* buttonState */) {
  switch (eventType) {
  case AceButton::kEventPressed:
    if (button-&gt;getPin() == BUTTON_MODE2_PIN) {
      mode = MODE_REGULATOR;
      printMode();
    }

    if (button-&gt;getPin() == BUTTON_MODE3_PIN) {
      mode = MODE_MANUAL;
      printMode();
    }
    break;
  case AceButton::kEventReleased:
    if (button-&gt;getPin() == BUTTON_MODE2_PIN) {
      mode = MODE_THERMOMETER;
      printMode();
    }
    if (button-&gt;getPin() == BUTTON_MODE3_PIN) {
      mode = MODE_REGULATOR;
      printMode();
    }
    break;
  }
}

void printMode()
{
  Serial.print(&quot;Mode &quot;);
  Serial.println(mode);
}

void handleButtonEvent(AceButton *, uint8_t, uint8_t);

void setup() {
  Serial.begin(9600);
  while (!Serial); // Wait until Serial is ready - Leonardo/Micro
  Serial.println(F(&quot;Test 3 boutons - la suite en 115200 bps&quot;));
  delay(300);

  // servo
  myservo.attach(11);

  // thermal probe
  sensors.begin();
  sensors.setWaitForConversion(false);
  sensors.setResolution(10);

  Serial.begin(115200);
  while (!Serial); // Wait until Serial is ready - Leonardo/Micro
  Serial.println(F(&quot;setup(): begin&quot;));

  // Initialize built-in LED as an output.
  pinMode(LED_PIN, OUTPUT);
  pinMode(SERVO_PIN, OUTPUT);

  // Buttons use the built-in pull up register.
  pinMode(BUTTON_SET_PIN, INPUT_PULLUP);
  pinMode(BUTTON_MORE_PIN, INPUT_PULLUP);
  pinMode(BUTTON_LESS_PIN, INPUT_PULLUP);

  pinMode(BUTTON_MODE2_PIN, INPUT_PULLUP);
  pinMode(BUTTON_MODE3_PIN, INPUT_PULLUP);

  buttonMode2.init(BUTTON_MODE2_PIN);
  buttonMode3.init(BUTTON_MODE3_PIN);

  // Configure the ButtonConfig with the event handler, and enable all higher
  // level events.
  ButtonConfig *buttonConfig = ButtonConfig::getSystemButtonConfig();
  buttonConfig-&gt;setEventHandler(handleButtonEvent);
  buttonConfig-&gt;setFeature(ButtonConfig::kFeatureClick);
  buttonConfig-&gt;setFeature(ButtonConfig::kFeatureLongPress);
  buttonConfig-&gt;setFeature(ButtonConfig::kFeatureRepeatPress);

  // Configs for the tune-up and tune-down buttons. Need RepeatPress instead of
  // LongPress.
  modeConfig.setEventHandler(handleModeEvent);
  modeConfig.setFeature(ButtonConfig::kFeatureClick);
  // These suppressions not really necessary but cleaner.
  modeConfig.setFeature(ButtonConfig::kFeatureSuppressAfterClick);

  // Check if the button was pressed while booting
  if (buttonMode3.isPressedRaw()) {
    mode = MODE_MANUAL;
    printMode();
  } else if (buttonMode2.isPressedRaw()) {
    mode = MODE_THERMOMETER;
    printMode();
  } else {
    mode = MODE_REGULATOR;
    printMode();
  }
  Serial.println(F(&quot;setup(): ready&quot;));
}

long timer_200 = 0;
long timer_1000 = 0;

void loop() {
  // Should be called every 4-5ms or faster, for the default debouncing time of ~20ms.
  buttonSet.check();
  buttonMore.check();
  buttonLess.check();
  buttonMode2.check();
  buttonMode3.check();

  // every 200 ms
  long now = millis();
  if (now &gt; timer_200 + 200) {
    timer_200 = now; // to reinit timer
    if (mode == MODE_MANUAL) {
      myservo.write(value);
    }
  }

  // every 1s
  if (now &gt; timer_1000 + 1000) {
    timer_1000 = now; // to reinit timer
    // get temperature requested last time
    Serial.print(&quot;Temperature for Device is: &quot;);
    Serial.println(sensors.getTempCByIndex(0));
    // and request a new one
    sensors.requestTemperatures(); // Send the command to get temperatures
    if (mode == MODE_THERMOMETER) {}
  }
}

// The event handler for both buttons.
void handleButtonEvent(AceButton *button, uint8_t eventType, uint8_t buttonState) {
  switch (eventType) {

  case AceButton::kEventClicked:
    if (button-&gt;getPin() == BUTTON_MORE_PIN) {
      value = value * 1.01;
      if (value &gt; max) {
        value = max;
      }

      Serial.println(value);
    }

    if (button-&gt;getPin() == BUTTON_LESS_PIN) {
      value = value * .99;
      if (value &lt; min) {
        value = min;
      }
      Serial.println(value);
    }

    if (button-&gt;getPin() == BUTTON_SET_PIN) {
      Serial.println(F(&quot;set&quot;));
    }
    break;

  case AceButton::kEventLongPressed:
    if (button-&gt;getPin() == BUTTON_SET_PIN) {
      Serial.println(F(&quot;set long press&quot;));
    }

    if (button-&gt;getPin() == BUTTON_MORE_PIN || button-&gt;getPin() == BUTTON_LESS_PIN) {
      tuneStart = millis();
    }
    break;
  case AceButton::kEventRepeatPressed:

    if (button-&gt;getPin() == BUTTON_MORE_PIN) {
      tuneUp();
    }

    if (button-&gt;getPin() == BUTTON_LESS_PIN) {
      tuneDown();
    }
    break;
  }
}

void tuneUp() {
  unsigned long duration = millis() - tuneStart;
  float ratio = 1 + (0.00001f * 4 * duration);
  value = value * ratio;
  if (value &gt; max) {
    value = max;
  }
  Serial.println(value);
}

void tuneDown() {
  unsigned long duration = millis() - tuneStart;
  float ratio = 1 - (0.00001f * 4 * duration);
  value = value * ratio;
  if (value &lt; min) {
    value = min;
  }
  Serial.println(value);
}
</code></pre>
<p>It's still quite a prototype.</p>
<p>What could cause this and how can I prevent it?</p>
 61722: <p>I suspect the 1Wire libary disables (and enables) interrupts when it sees fit. This will cause servo &quot;hickups&quot; because the servo library uses interrupts.</p>
<p>If I'm right then your best bet is using a servo library that doesn't use interrupts. I always use a hardware PWM library for driving servos, so I have no servo library recommendations to make, but maybe somebody else has.</p>
<p>Also: you are doing a <code>Serial.begin()</code> twice, and the variables you use for timing (using <code>millis()</code>) should be <code>unsigned long</code>, not <code>long</code>.</p>
