Arduino controlling a Brushless DC Motor via an ESC
Brushless DC Motor :
A brushless DC motor (BLDC) requires a driver called Electronic Speed Controller (ESC) to power and spin it. Hence, controlling the ESC we can control the BLDC motor.ESC:
The ESC feeds a 3-phase electrical sine wave pulse to the motor via the three wires (Black, yellow, and red wire) of the motor. Varying the frequency of the sine wave, you can varies the speed of the motor, higher the frequency the higher the speed of the motor. Everything about running the motor is taken care by ESC.Therefore on this topic we will not cover in details how the ESC spin the motor but we are interested what are the "inputs" needed by the ESC in order for it to run, speedup, slowdown, or stop the motor.
ESC INPUTS:
While the output of ESC is a sine wave to run the motor, its input is a square wave or PWM wave.For the ESC to work, we need two things.
- Arm the ESC, by creating a minimum pulse of 1100us (Typical on most ESC) at power up. Once the ESC recognizes this pulse (about 1 to 3 sec from power on of MCU), it creates a 3 fast beeps which tells you that it is armed, and ready to Rock! - the ESC wont spin or power the motor yet!
- Once Armed, you can increase or decrease the pulse in the range 1100us to 1900us. This will turn the motor slower at shorter pulse (above 1100us) and faster at longer pulse max 1900us. (NOTE: Typically ESC is designed to power up or spin the motor at about 1180us.)
HOW ARDUINO CREATES A PULSE:
The Arduino can create a pulse in many ways from simple sketch to a more advance but precise sketch which will be presented next....Power and Wiring Setup:
Buttons: There are three button (connected at pin 2, 3, and 4 via blue wire) act as a speed controller switch i.e. to control the pulse the Arduino feeds to ESC data wire (orange wire). Each buttons is equipped with 5k pull-down resistors (connected to common GND pin via green wire) to avoid stray current going to input pins.ESC: The Electronic Speed Controller (ESC) has three main wire as follows:
- The power input 2-thick-wire (-Black and + Red ), hooked to a Battery (Typical Lipo battery provides 7.4v, 11.1v, 14.8v, etc.) to provide power to ESC, Motor, and the Battery eliminator wire.
- The output 3-thick-wires (Black, Yellow, and Green) which provide power to the motor - however the output power is a regulated by ESC in a sine wave.
- The 5v output wire (thin Red) also called "battery eliminator circuit" but this is simply a regulate step down power to supply 5v electronics when needed.
- The Data Input wire is the control wire of the ESC, the wire receives and accepts square wave PWM signal which the ESC interprets and process to create a sine wave output to motor.
Arduino Sketch:
As discussed, the microcontroller must create and feed PWM pulse to ESC data line. The sketch below Version 1.0 will create a default pulse 1100us to arm the ESC at 50Hz then can be incremented or decremented by 1us at button press.
Version 1.0:
int Pulse=1100; //minimum pulse for arming ESC
int wait=0; // delay time to wait human to acknowledge the pressvoid setup ()
{ Serial.begin(9600);
pinMode(2, INPUT);
pinMode(3, INPUT);
pinMode(4, INPUT);
pinMode(6, OUTPUT);
pinMode(13, OUTPUT);
} // end of setup
void loop ()
{
//The function that control the speed of the motor
Speedy();
//This is the sketch that generate PWM
digitalWrite(6, HIGH);
delayMicroseconds(Pulse);
digitalWrite(6, LOW);
delayMicroseconds(10000-Pulse);
delayMicroseconds(10000);
Serial.println(Pulse); //So you can view Pulse on serial window
} // end of loop
void Speedy()
{digitalWrite(13, LOW);
//Scan for button switch starts here:
if (digitalRead(2)==HIGH)//Start Speedup button at Pin 2
{wait=wait+1;
if (wait>10) {Pulse=Pulse+1; wait=0;digitalWrite(13, HIGH);}
}//End speedup button at Pin 2
else if (digitalRead(3)==HIGH)//Start slow down button at Pin 3
{wait=wait+1;
if (wait>10) {Pulse=Pulse-1; wait=0; digitalWrite(13, HIGH);}
if (Pulse<1100) {Pulse=1100;}//In any case the ESC will remain Arm
}//End slow down button at Pin 3
else if (digitalRead(4)==HIGH) //Cut-off button at Pin 4
{Pulse=1100; digitalWrite(13, HIGH);}
else {Pulse=Pulse;digitalWrite(13, LOW);}//If no button press, pulse remains as last pulse
}
The above is the simplest sketch to create a PWM signal using digitalWrite() function and will be fine in most non-demanding or non-precision application.
Improving to Version 1.1:
For a more efficient speed control, we will be upgrading the function Speedy() by creating a delay function incWait() (different from the built-in delayMicroseconds() function of Arduino) without affecting the PWM when pressing the button, that is the incWait() will not pause or stop the loop() function from looping.
int Pulse=1100;
int Delay=0;double wait=0;
double timePrev, timeNow;
void setup()
{ Serial.begin(9600);
pinMode(2, INPUT);
pinMode(3, INPUT);pinMode(4, INPUT);
pinMode(6, OUTPUT);
pinMode(13, OUTPUT); delay(500);
}
void loop()
{
//This is the sketch that generate PWM
digitalWrite(6, HIGH);
delayMicroseconds(Pulse);
digitalWrite(6, LOW);
delayMicroseconds(10000-Pulse);
delayMicroseconds(10000);
Speedy();
}
{
//Scan for button switch starts here:
if (digitalRead(2)==HIGH && Delay==0)
{ Pulse=Pulse+1; digitalWrite(13, HIGH); Delay= 1; timeNow=millis();
} //End speedup button at Pin 2
else if (digitalRead(3)==HIGH && Delay== 0)
{ Pulse=Pulse-1; digitalWrite(13, HIGH); Delay=1; timeNow=millis();
if (Pulse<1100) {Pulse=1100;}//In any case the ESC will remain Arm
}//End speed down button at Pin 3
{
Pulse=1100; digitalWrite(13, HIGH); Delay=1; timeNow=millis();
}
else {Pulse=Pulse;} //If no button press, pulse remains the same as previous
incWait(); //Always checking Delay
}
//Delay function without pausing/delay on the loop() function
void incWait()
{ if ((wait)<200 && Delay==1)
{ timeNow=millis();
wait = timeNow - timePrev;
Delay= 1;
//Data view for debug purposes only
Serial.print((wait));Serial.print("\t\t");
Serial.print(timeNow);Serial.print("\t\t");
Serial.print(timePrev);Serial.print("\t\t");
Serial.println(Pulse);
}else
{ Delay= 0; wait =0, timeNow=0, timePrev=millis();
digitalWrite(13, LOW);
//Data view for debug purposes only
Serial.print((wait));Serial.print("\t\t");
Serial.print(timeNow);Serial.print("\t\t");
Serial.print(timePrev);Serial.print("\t\t");
Serial.println(Pulse);
}}
Updates: Version 1.1 has better delay timing function with introduction of function incWait() compare to Version 1.0 which uses loop iteration to delay the button press. However either can have it's practical advantage depending on purpose.
The main purpose we use button switch on this application is to precisely control the speed of motor by precise incrementing the pulse (widen to speedup or narrow it to slow down) at even smallest amount, in this case at every 1us per press (you can lower it to eve less say 500 nanoseconds per press). One application I can think of is testing or gathering data of motor performance at increment power.
Then the very purpose of providing delay upon buttons press is to avoid unnecessary increments. On this example the microcontroller can loops at 50 times a second, 50Hz, or at every 20ms . At this processing speed and without using delay, pressing the button can create multiple increment before the slow "human" (you) can release the button.
Improving to Version 2.0
Version 2.0 focus on improving the PWM by using the Arduino servo library to create the pulse. This versions now uses two virtual clock, the Timer0 which is the default timer uses by loop() functions and Timer1 which is solely use by servo library that creates the pulse PWM.
#include <Servo.h>
int Pulse=1100; //Default
for Arming ESC
int Delay=0;float timePrev=0, timeNow=0, wait=0;
Servo myservo; // create servo object to control a servo
void setup()
{
pinMode(2, INPUT);
pinMode(3, INPUT);
pinMode(4, INPUT);
pinMode(13, OUTPUT);
myservo.attach(6); // attaches the servo on pin 9 to the servo object
}
void loop()
{
myservo.writeMicroseconds(Pulse);
Speedy();pinMode(3, INPUT);
pinMode(4, INPUT);
pinMode(13, OUTPUT);
myservo.attach(6); // attaches the servo on pin 9 to the servo object
}
void loop()
{
myservo.writeMicroseconds(Pulse);
}
{
//Scan for button switch
starts here:
if (digitalRead(2)==HIGH && Delay== 0)
if (digitalRead(2)==HIGH && Delay== 0)
{ Pulse=Pulse+1; digitalWrite(13, HIGH); Delay= 1; timeNow =millis();
}
//End speedup button at Pin 2else if (digitalRead(3)==HIGH && Delay== 0)
{ Pulse=Pulse-1; digitalWrite(13, HIGH); Delay=1; timeNow =millis();
if (Pulse<1100) {Pulse=1100;}//In any case the ESC will remain Arm
}//End speed down button at Pin 3
else if (digitalRead(4)==HIGH) //Cut-off motor button at Pin 4
{
Pulse=1100; digitalWrite(13, HIGH); Delay=1; timeNow =millis();
}
else {
Pulse=Pulse;
} //If no button press, pulse remains as it is
incWait(); //Always checking button Delay
}if (Delay==1 && wait<200)
{ timeNow=millis();
wait = timeNow - timePrev;
Delay= 1;
Serial.print(wait); Serial.print("\t");
Serial.print(Delay); Serial.print("\t");
Serial.print(timeNow);
Serial.print(timePrev);
Serial.println(Pulse);
}
else
{
Delay= 0; wait =0; timePrev = millis();digitalWrite(13, LOW);
Serial.print(wait); Serial.print("\t"); Serial.print(Delay); Serial.print("\t");
Serial.print(timeNow);
Serial.print(timePrev);
Serial.println(Pulse);
}
}
Updates: Unlike the version 1.0 and 1.1 which uses Timer0 to create the pulse, this version uses Timer1 (independent with Timer0) which has higher resolution. In some way to say, there are now two virtual clock working at onetime - more flexibility.
Advantages and Disadvantages
this part in progress.......