Servo not working with DC motor on Photon 2

This is a problem specific to the Photon 2 because this code worked fine on the Argon.

I have students build a rotating fan with a servo and a DC motor. I have confirmed that the servo code works by itself and the DC motor code works by itself. However, when I combine the two and use millis() to control the servo rotation, the servo freezes and makes a grinding sound. However, when I use delay() of millis(), everything works fine.

Here is the code with millis() that causes the servo to freeze

const int AIN1 = D3; 
const int AIN2 = D4; 
const int PWMA = A5; 
const int PIN_SERVO = A2;

Servo fanServo;
int servoAngle = 90;
unsigned long prevMillis = 0;
bool isAngleIncreasing = true;

void loop() {
    analogWrite(PWMA, 255);
    digitalWrite(AIN1, HIGH);
    digitalWrite(AIN2, LOW);

    unsigned long currMillis = millis();
    if (currMillis - prevMillis > 1000) {
        prevMillis = currMillis;
        if (isAngleIncreasing == true) {
            servoAngle += 5;
        } else {
            servoAngle -= 5;
        }
        if (servoAngle >= 165) {
            isAngleIncreasing = false;
        } else if (servoAngle <= 15) {
            isAngleIncreasing = true;
        }
        fanServo.write(servoAngle);
    }
}

and here is the same code using delay() that works fine

const int AIN1 = D3; 
const int AIN2 = D4; 
const int PWMA = A5; 
const int PIN_SERVO = A2;
Servo fanServo;
int servoAngle = 90;
unsigned long prevMillis = 0;
bool isAngleIncreasing = true;

void loop() {
    analogWrite(PWMA, 255);
    digitalWrite(AIN1, HIGH);
    digitalWrite(AIN2, LOW);

    if (isAngleIncreasing == true) {
        servoAngle += 5;
    } else {
        servoAngle -= 5;
    }
    if (servoAngle >= 165) {
        isAngleIncreasing = false;
    } else if (servoAngle <= 15) {
        isAngleIncreasing = true;
    }
    fanServo.write(servoAngle);
    delay(200);
}

Am I missing something?

@rob7, if you look at the mills() based timer, you will see that this block runs on EVERY loop() or approx. every millisecond!

    analogWrite(PWMA, 255);
    digitalWrite(AIN1, HIGH);
    digitalWrite(AIN2, LOW);

But the servo decision code within the if (currMillis - prevMillis > 1000) only runs every second.

The delay() based code, however, runs the above code AND the servo decision code every 200ms.

You may want to think about the timing of each part of your code and then use the millis() delay accordingly.

The millis() logic appears to be correct. I ran it with logging added:

        fanServo.write(servoAngle);
        Log.info("fanServo=%d", servoAngle);

and the logs look correct:

0000012460 [app] INFO: fanServo=145
0000013461 [app] INFO: fanServo=150
0000014462 [app] INFO: fanServo=155
0000015463 [app] INFO: fanServo=160
0000016464 [app] INFO: fanServo=165

Thanks for your response, @peekay123 . That makes sense but was actually by design. I simplified the code I posted here because in my origianl I was using a potentiometer to control the DC motor speed and I wanted that to happen responsively where as servo rotation would happen once per second. Here was the actual code

void loop() {
    digitalWrite(AIN1, HIGH);
    digitalWrite(AIN2, LOW);
    int potVal = analogRead(PIN_POT);
    int motorSpeed = map(potVal, 0, 4095, 0, 255);
    analogWrite(PWMA, motorSpeed);

    unsigned long currMillis = millis();
    if (currMillis - prevMillis > 1000) {
        prevMillis = currMillis;

        if (isAngleIncreasing == true) {
            servoAngle += 5;
        } else {
            servoAngle -= 5;
        }
        if (servoAngle >= 165) {
            isAngleIncreasing = false;
        } else if (servoAngle <= 15) {
            isAngleIncreasing = true;
        }
        fanServo.write(servoAngle);
    }
}

If I put the DC motor analogWrite is in the millis block then there will be a delay in the responsiveness of the pot.

I guess I don't see why the DC motor + pot works fine when it isn't in the millis block, but the servo is freezing (and is inside the millis block).

Thanks. Yes, if I comment out the DC motor code (digitalWrite and analogWrite, the servo slowly rotates as expected).

I think the problem is that the RTL872x uses a single PWM timer, which is also used for Servo.

The problem is likely that PWM has a default frequency of 500 Hz but Servo is 50 Hz. When you set analogWrite PWM, it's changing the frequency used by servo from 50 to 500 Hz, which would explain the behavior you are seeing.

The solution is probably to use the three parameter version of analogWrite and set to third parameter to 50 for motor speed, if that is compatible with your motor.

3 Likes

Thank you @rickkas7 ! That was exactly the problem! This worked
analogWrite(PWMA, motorSpeed, 50);

2 Likes

Docs updated with a note about using analogWrite and Servo on Gen 4 devices.

3 Likes

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