BEMF BLDC motor with ESP32 (sensorless)

Hello everyone,

I've started a project to control a sensorless motor using an ESP32 with the BEMF method. Everything is functioning, and the motor runs, but I encounter an issue during startup. I need to manually assist the motor to ensure it rotates in the correct direction. If I don't do this, the motor tends to spin in the wrong direction, gets stuck, and then starts turning again in the wrong direction...

Could someone please help me identify where I might be going wrong? Here’s my code below:

#include <Arduino.h>

#define SETPOINT_COEFF  1.10*6

#define enable    18
#define setpoint  34
#define i_meas    17
#define i_fault   4
#define tsw_fault 5
#define v_ts      16
#define v_bus     33
#define v_u       39
#define v_v       35
#define v_w       32

#define U_H       13
#define V_H       14
#define W_H       26
 
#define U_L       12
#define V_L       27
#define W_L       25

void bldc_start();
void bldc_move();
void phase1();
void phase2();
void phase3();
void phase4();
void phase5();
void phase6();

int bldc_step=0;
unsigned int i_start;
uint16_t motor_reset=0;
byte changeINT_flag=0;
uint8_t motor_speed=100;


void IRAM_ATTR intU_FALLING()
{
  motor_reset=0;
  byte j=0;                         // Cette variable est utilisée pour rompre la boucle en cas d'échec de sortie de l'interruption.
  for(byte i = 0; i < 30; i++)      // si l'enroulement U tombe sur le front descendant mais qu'un arc se produit quand même, 
  {                                 // il attend que l'arc se termine, c'est-à-dire que l'enroulement centre exactement l'aimant.                              
    if(digitalRead(v_u))  i -= 1;
    j++;
    if(j>200)break;        
  }
  bldc_move();                      // alimenter les enroulements dans la séquence de phases suivante.
  bldc_step++;
  if(bldc_step>6) bldc_step=1;    
}
 
void IRAM_ATTR intU_RISING()
{
  motor_reset=0;
  byte j=0;
  for(byte i = 0; i < 30; i++)
  {       
    if(!digitalRead(v_u))  i -= 1;        
    j++;
    if(j>200)break;
  }
  bldc_move(); 
  bldc_step++;  
  if(bldc_step>6) bldc_step=1;     
}
 
void IRAM_ATTR intV_FALLING()
{
  motor_reset=0;
  byte j=0;
  for(byte i = 0; i < 30; i++)
  {    
    if(digitalRead(v_v))  i -= 1; 
    j++;
    if(j>200)break;       
  }
  bldc_move(); 
  bldc_step++;  
  if(bldc_step>6) bldc_step=1;     
}

void IRAM_ATTR intV_RISING()
{
  byte j=0;
  motor_reset=0;
  for(byte i = 0; i < 30; i++)
  {    
    if(!digitalRead(v_v))  i -= 1; 
    j++;
    if(j>200)break;        
  }
  bldc_move(); 
  bldc_step++;  
  if(bldc_step>6) bldc_step=1;      
}

void IRAM_ATTR intW_FALLING()
{
  byte j=0;
  motor_reset=0;
  for(byte i = 0; i < 30; i++)
  {    
    if(digitalRead(v_w))  i -= 1;  
    j++;
    if(j>200)break;       
  }
  bldc_move(); 
  bldc_step++;  
  if(bldc_step>6) bldc_step=1;      
}

void IRAM_ATTR intW_RISING()
{
  motor_reset=0;
  byte j=0;
  for(byte i = 0; i < 30; i++)
  {    
    if(!digitalRead(v_w))  i -= 1; 
    j++;
    if(j>200) break;        
  }
  bldc_move(); 
  bldc_step++;  
  if(bldc_step>6) bldc_step=1;      
}

void phase1()
{    
  digitalWrite(U_L,LOW);
  digitalWrite(V_L,HIGH);
  digitalWrite(W_L,LOW);

  ledcWrite(0,motor_speed);
  ledcWrite(1,0);
  ledcWrite(2,0);

  if(changeINT_flag==0)
  {
    detachInterrupt(v_u);
    detachInterrupt(v_v);
    attachInterrupt(v_w, intW_FALLING, FALLING);  
  }
}

void phase2()
{
  digitalWrite(U_L,LOW);
  digitalWrite(V_L,LOW);
  digitalWrite(W_L,HIGH);

  ledcWrite(0,motor_speed);
  ledcWrite(1,0); 
  ledcWrite(2,0);    

  if(changeINT_flag==0)
  {
    detachInterrupt(v_u);
    detachInterrupt(v_w);
    attachInterrupt(v_v, intV_RISING, RISING);
  }    
}

void phase3()
{
  digitalWrite(U_L,LOW);
  digitalWrite(V_L,LOW);
  digitalWrite(W_L,HIGH);

  ledcWrite(0,0);
  ledcWrite(1,motor_speed); 
  ledcWrite(2,0);      

  if(changeINT_flag==0)
  {
    detachInterrupt(v_v);
    detachInterrupt(v_w);
    attachInterrupt(v_u, intU_FALLING, FALLING); 
  }    
}

void phase4()
{
  digitalWrite(U_L,HIGH);
  digitalWrite(V_L,LOW);
  digitalWrite(W_L,LOW);

  ledcWrite(0,0);
  ledcWrite(1,motor_speed); 
  ledcWrite(2,0);    
    
  if(changeINT_flag==0)
  {
    detachInterrupt(v_u);
    detachInterrupt(v_v);
    attachInterrupt(v_w, intW_RISING, RISING);
  }   
}

void phase5()
{
  digitalWrite(U_L,HIGH);
  digitalWrite(V_L,LOW);
  digitalWrite(W_L,LOW);

  ledcWrite(0,0);
  ledcWrite(1,0); 
  ledcWrite(2,motor_speed); 

  if(changeINT_flag==0)
  {
    detachInterrupt(v_u);
    detachInterrupt(v_w);
    attachInterrupt(v_v, intV_FALLING, FALLING);
  }     
}

void phase6()
{
  digitalWrite(U_L,LOW);
  digitalWrite(V_L,HIGH);
  digitalWrite(W_L,LOW);

  ledcWrite(0,0);
  ledcWrite(1,0); 
  ledcWrite(2,motor_speed); 

  if(changeINT_flag==0)
  {
    detachInterrupt(v_v);
    detachInterrupt(v_w);
    attachInterrupt(v_u, intU_RISING, RISING);  
  }        
}

void bldc_move()
{  
  switch(bldc_step)
  {
    case 1: phase1(); break;
    case 2: phase2(); break;
    case 3: phase3(); break;
    case 4: phase4(); break;
    case 5: phase5(); break;
    case 6: phase6(); break;
    default: break;   
  }    
}

void bldc_start()
{   
  i_start=5000;                         // La commutation est d'abord appliquée aux enroulements à intervalles de 8 ms.
  bldc_step=0;  
  while(i_start>500)                   // La commutation appliquée aux enroulements s'effectue en la diminuant à 1 ms.
  {
    bldc_step++;
    if(bldc_step>6) bldc_step=1;
    bldc_move();
    delayMicroseconds(i_start);
    i_start-=20;                        // Le temps entre chaque phase en commutation est réduit de 20 uS.
  }    
}

/*------------------------------------------------------------------------*/
void setup() 
{
  pinMode(setpoint, INPUT);
  pinMode(i_meas, INPUT);
  pinMode(i_fault, INPUT);
  pinMode(tsw_fault, INPUT);
  pinMode(v_ts, INPUT);
  pinMode(v_bus, INPUT);
  pinMode(v_u, INPUT);
  pinMode(v_v, INPUT);
  pinMode(v_w, INPUT);

  pinMode(U_L,OUTPUT);
  pinMode(V_L,OUTPUT);
  pinMode(W_L,OUTPUT);
  
  pinMode(enable, OUTPUT);
  digitalWrite(enable, HIGH);

  Serial.begin(115200);
  delay(1000);

  ledcAttachPin(U_H, 0);
  ledcAttachPin(V_H, 1);
  ledcAttachPin(W_H, 2);

  ledcSetup(0, 50000, 8);
  ledcSetup(1, 50000, 8);
  ledcSetup(2, 50000, 8);
 
  changeINT_flag=1;
  bldc_start();
  changeINT_flag=0;
  bldc_step=6;
  attachInterrupt(v_u, intU_RISING, RISING);

}

/*------------------------------------------------------------------------*/
void loop() 
{
  static unsigned long lastEventTime1 = millis();
  static const unsigned long INTERVAL1_MS = 500;

  if ((millis() - lastEventTime1) > INTERVAL1_MS) 
  {
    float setpointREAD=SETPOINT_COEFF*3.3*(float(analogRead(setpoint))/4095);

    motor_speed=setpointREAD*10;
    if(motor_speed>=100) motor_speed=100;
    if(motor_speed<=25) motor_speed=25;

    Serial.print("PWM=");Serial.println(motor_speed);
    
    if(motor_speed<=25 || digitalRead(v_ts)==LOW || digitalRead(tsw_fault)==LOW) digitalWrite(enable, HIGH);
    else digitalWrite(enable, LOW);

    motor_reset++;
    if(motor_reset>10)
    {
      motor_reset=0;
      changeINT_flag=1;      
      detachInterrupt(v_u); detachInterrupt(v_v); detachInterrupt(v_w);
      motor_speed=100; 
      bldc_start(); 
      changeINT_flag=0;
      bldc_step=6;  
      attachInterrupt(v_u, intU_RISING, RISING);       
    } 

    lastEventTime1 = millis();
  }
}

Where is your schematic to go with the code? How are you powering the motor and the ESP32?

1 Like

BEMF.pdf (96,9 Ko)
POWER.pdf (108,8 Ko)
Hi Paul, the schematics are attached (only BEMF and POWER parts) ESP32 and power supply are done using Wroom module. for the power supply, the ESP is powered by 3v3 and the motor with a high voltage up to 320Vdc, but for tests I use 120Vdc. for the BEMF and POWER parts I use a 15Vdc supply.
My problem occurs only when starting, if I help is, the motor runs correctly.

I believe the issue is with the startup procedure; sometimes the motor fails to start properly and just vibrates.
Has anyone else experienced the same problem?

Looking at the missing schematic it appears you do not have enough phase shift to get it started. Add about 10 - 20 degrees and see if that helps.

Thanks gilshultz,
If I understand correctly, I need to add some delay between energizing phases, is that correct?
I tried a method proposed by Infineon, where 'two phases are held to ground and one is switched to high.' The motor's behavior improved, but sometimes, when restarting, it vibrates with a loud noise. When it vibrates, it’s already in the loop(), which means the start-up procedure was completed.
My new bldc_start() code:

void bldc_start()
{   
  /*i_start=20000;                         // La commutation est d'abord appliquée aux enroulements à intervalles de 8 ms.
  bldc_step=5;  
  while(i_start>1000)                    // La commutation appliquée aux enroulements s'effectue en la diminuant à 1 ms.
  {
    bldc_step++;
    if(bldc_step>6) bldc_step=1;
    bldc_move();
    delayMicroseconds(i_start);
    i_start-=1000;                        // Le temps entre chaque phase en commutation est réduit de 20 uS.
  }*/
  
  bldc_step=1;
  phase1();
  digitalWrite(W_L,HIGH);
  delay(100);
  bldc_step=2;
  phase2();
  delay(100);
  bldc_step=3;
  phase3();
  delay(100);
  bldc_step=4;
  phase4();
  delay(100);
  bldc_step=5;
  phase5();
  delay(100);
  bldc_step=6;
  phase6();
}

RESOLVED !! Just by adding a ramp-up start-up.

1 Like