Let's start by a video 

the idea

I had an old wiper motor made by Bosch. It's the same type as this one.

They are very powerful, they draw 2 to 3A, do not run very fast but have really a lot of torque, much more than what I would ever need to rotate a solar tracker (even a big size one !).

However these motors are DC ones without any solution to control their position. 

So I decided to add a cheap 12bits magnetic encoder AS5600

This chip is equiped with a precise hall sensor and allows to detect the angualr position of a diametric magnet "flying" above it (ideally less than 3mm above the chip).

I decided to install the magnet directly on the motor shaft (not the wiper shaft) to get the maximum precision.


mounting the magnet

The first operation was to cut the end cap of the motor to access the motor shaft.

Unmount the motor, remove the cover. Then drill the bottom (not the bearing...) and finish grinding it so that the shaft will be visible.

Now, print the sensor holder and glue it in place. 

Also on thingiverse: https://www.thingiverse.com/thing:5552935

And finally glue the magnet and fix the sensor in place

Your motor is now a servo motor !

wiring the AS5600

this device is an I2C sensor and needs only 4 pins to interface with ESP32 MCU


Adding a driver board 

To drive this motor you will need a quite powerful H bridge driver. I chose the IBT-2 chineese driver.

It's a very powerful one said to handle 43A.

It's composed of two half bridges BTN7970

Using this chip is quite easy. I followed this excellent tutorial but replaced the arduino by an ESP32.


Wiring IBT-2 with ESP32

The big terminal blocks are connected to the motor and the DV power (12V in reality !)

The pin headers are connected as on the drawing to the ESP32 pins


ESP32 firmware 

Example firmware is available on my Github

The code is extremely simple and only shows how to read the sensor, drive the motor and apply a PID control loop to precisely position the motor at any number of turn you want (integer + fractional turn of the motor shaft).

reading the AS5600

I do use Rob Tillard's AS5600 library. It is really simple and does the job !

A few lines are enough to read the sensor and detect "zero crossing" for full rotations counting:

 //AS5600


  rawValue = as5600.readAngle();
  if (((rawValue - prevRawValue) < -999) && CW) nbRot++ ; //apply hysteresis to detect each full rotation (4095 <--> 0)
  if (((rawValue - prevRawValue) > 999) && !CW) nbRot-- ;


  prevRawValue = rawValue; //save the rawValue for next iteration

driving the motor

no library but simple code as well !

two PWM signals enter Left and Right H bridges.

void runMotor(void)
{
  if (pwmSpeed > 0)
  {
    ledcWrite(0, pwmSpeed);
    ledcWrite(1, 0);
    CW = true;
  }
  else
  {
    ledcWrite(0, 0);
    ledcWrite(1, -pwmSpeed);
    CW = false;
  }
}

the pwmSpeed variable can be positive or negative. Positive is for CW rotation and negative CCW.

each pwm signal has a range 0-2047 on 11 bits and a frequancy of 24kHz

They are "hardware PWM" embeded into the ESP32

  ledcAttachPin(RPWM_PIN, 0); // assign PWM pins to channels
  ledcAttachPin(LPWM_PIN, 1); // assign PWM pins to channels
  // Initialize channels : ledcSetup(uint8_t channel, uint32_t freq, uint8_t resolution_bits);
  ledcSetup(0, 24000, 11); // 24 kHz PWM, 11-bit resolution (range 0-2047)
  ledcSetup(1, 24000, 11);

Now that we can rotate the motor, we simply have to control its motion.

This is done with a PID control loop

I do use Brett Beauregard's excellent PID library. It is provided with very clever tutorial that I highly recommend. 

Using this library is really simple. 

I have tuned the PID to have a "soft" behavior and to minimize overshoot. This is done taking advantage of the motor "deadband". Voltage at which the motor stalls.

I do start with a fully proportonnal PID tunind and when entering the deadaband I do switch to an almost "integral" PID to recover the positionning error.

  Kp =  50;                     // start with a fully proportionnal PID
  Ki = 0.0;
  if (abs(pwmSpeed) < DEADBAND) //switch to almost full integral, needed to skip "fast" the motor deadband with no motion...
  {
    Kp =  5;
    Ki = 100;
  }
  myPID.SetTunings(Kp, Ki, Kd);
  myPID.Compute();

the PID is refreshed every 1ms and this garantees a very accurate control of the shaft position.

The overshoot is minimal and almost constant to 0.46 rotation of the motor. 

 And it gives  really good results. The motor stops almost exactly at the desired value

Precision of the control loop

I have run the code several times, logging setpoint, reached number of rotation, sensor value and fractionnal number of rotation got.

The program consists in positionning the shaft at +300 rotations (absolute position) then going back to -300 (absolute).... and so on. 

We finally get an average of 0,33 turn (motor shaft position) and a standard deviation of 0,09 turn

Knowing that 600 rotations give 11,5 rotation of the wiper shaft and assuming that I will attach a 8mm leadscrew with  standard 1,25mm per turn, we get and absolute position (average) on the leadscrew of:

0.46*11.5/600*1.25 = 0,011mm

0,011 mm on the leadscrew

Not so bad :-) (but for sure we will never get it as the mechanics will never be "perfect": backlash, dilatation...).

However we can consider that we have an "absolute" servo motor with a totally controlled position.


improving the control loop

I have tuned the PID loop with a much stronger set of parameters and with an "inertia" coefficient to precisely balance the little overshoot when the motor is stopping.

Details can be found into this log.

The position of the shaft is now controlled in a much accurate way.

The target postion can be "decimal" (-150,82) 

And if I drive a 8mm leadscrew with 1.25mm step I will get a positionning error of 23µm 

-2,3-05 mm on the output shaft driving a 8mm leadscrew

Well as usual this is theoritical as I don't take into account backlash, thermal dilatation and so on.

But this is impressively good !

Conclusion

This very simple project demonstrates that we can modify this cheap (10 to 20€) motor to transform it into a very precise and powerful servo motor. It could easily be used to act as a "digitally controlled" linear actuator.

If you don't believe me and If you want to see how strong could be a wiper motor linear actuator, have a look at this video: