Drone project with 2 Brushless motors

Hello everyone,
I'm doing a project involving the angular stabilization of a drone using the tutorial by electronoobs(PID control arduino drones mpu6050 mpu9250 gyro accelerometer)
I used two Black Widow 2206, an MPU6050 and an Arduino Uno card . However, I had a problem with the code for the motors: only one of them rotate.
Here is my code( sorry but the comments are not in English):

#include <Servo.h>
#include <Wire.h>

Servo ESC;     // create servo object to control the ESC
Servo ESC2;


int16_t Acc_bruteX, Acc_bruteY, Acc_bruteZ, Gyr_bruteX, Gyr_bruteY, Gyr_bruteZ;

float angle_Acceleration[2];
float Angle_Gyro[2];
float angle_Total[2];

float Tempspasse, time, tempsPreced;
int i;
float rad_a_deg = 180/3.141592654;

float PID, pwmGauche, pwmDroit, erreur, erreur_avant;
float pid_p = 0;
float pid_i = 0;
float pid_d = 0;

////////////PID constantes////////////////////////////////////////
float kp = 3.55;
float ki = 0.005;
float kd = 2.05;
//////////////////////////////////////////////////////////////////

float vitesse=1500; // valeur de la vitesse de tour des moteurs
float angle_souhaite = 0; // on veut que la balance reste parrallele au sol


void setup()  {
  Wire.begin(); //commence la communication par fil
  Wire.beginTransmission(0x68);  //envoi l'addresse esclave (0x68)
  Wire.write(0x6B); // accéder au registre 6B qui s'occupe du management de la force des moteurs
  Wire.write(0);
  Wire.endTransmission(true);
  Serial.begin(115200);
  ESC.attach(9); // (on attache l'ESC du moteur 1 a la pin 9)
  ESC2.attach(3);  // (on attache l'ESC du moteur 2 a la pin 3)

  time=millis(); // compte le temps en milliseconde
  //pour que les ESC démarre  nous avons besoin d'une valeur minimum qui est 1000us et et une valeur max de 2000us
  ESC.writeMicroseconds(1000);
  ESC2.writeMicroseconds(1000);
  delay(500); // donne du temps pour tout connecter et une dernière verification avant que tout commence. 
}


void loop(){

///////////////////////////////IMU/////////////////////////////////////////


   tempsPreced = time; // le temps précédant est enregistré et gardé avant que le vrai temps soit lu
   time = millis(); // temps actuel Ă  lire 
   Tempspasse=(time - tempsPreced)/1000;
// le Tempspassé est le temps passé depuis la dernière boucle,on divise par 1000 pour obtenir des secondes depuis les millisecondes 

 Wire.beginTransmission(0x68);
 Wire.write(0x3b); // demande le registre 0x3b et répond avec l'accélération selon x
 Wire.endTransmission(false);
 Wire.requestFrom(0x68,6,true); // l'appareil maitre demande Ă  l'appareil esclave 
                                //(MPU6050) les 6 bytes qu'il détient (accélération et gyro)

 Acc_bruteX=Wire.read()<<8|Wire.read(); // chaqu'une des valeurs Ă  besoin de deux registres
 Acc_bruteY=Wire.read()<<8|Wire.read();  
 Acc_bruteZ=Wire.read()<<8|Wire.read();  

// on calcul les angles avec l'equation d'Euler 
//la datasheet de la MPU6050 nous donne la valeur 16384.0 qui doit diviser nos valeurs pour nous donner des valeurs en g
//pour obtenir des degrées nous utilisons notre variable rad_a_deg
// on applique la formule d'euler
/*---X---*/
angle_Acceleration[0] = atan((Acc_bruteY/16384.0)/sqrt(pow((Acc_bruteX/16384.0),2)) + pow((Acc_bruteZ/16384.0),2))*rad_a_deg;
/*---Y---*/
angle_Acceleration[1] = atan(-1*(Acc_bruteX/16384.0)/sqrt(pow((Acc_bruteY/16384.0),2)) + pow((Acc_bruteZ/16384.0),2))*rad_a_deg;

// on fait maintenant la mĂŞme chose pour les valeurs du Gyro,
// les valeurs du Gyro commence Ă  0x43 et nous avons que 4 valeurs (pas besoin du Z, l'axe de lacet)

 Wire.beginTransmission(0x68);
 Wire.write(0x43); //addresse des valeurs du gyro
 Wire.endTransmission(false);
 Wire.requestFrom(0x68,4,true); //on a besoin de juste 4 valeurs 
   
 Gyr_bruteX=Wire.read()<<8|Wire.read(); // encore une fois on déplace les valeurs 8 fois vers la gauche 
 Gyr_bruteY=Wire.read()<<8|Wire.read();


// maintenant pour obtenir les valeurs du gyro il faut diviser par 131 parce que c'est le nombre que nous donne la datasheet

 /*---X---*/
Angle_Gyro[0]=Gyr_bruteX/131.0;
/*---Y---*/
Angle_Gyro[1]=Gyr_bruteY/131.0;

//maintenant pour obtenir des degrés, on doit multiplier ces degrés/secondes par le Tempspassé
//on peut donc apppliqué le dernier filtre

/*--- angle axe X---*/
angle_Total[0]=0.98*(angle_Total[0]+ Angle_Gyro[0]*Tempspasse)+ 0.02*angle_Acceleration[0];
/*--- angle axe Y---*/
angle_Total[1]=0.98*(angle_Total[1]+ Angle_Gyro[1]*Tempspasse)+ 0.02*angle_Acceleration[1]; 
// on a maintenant nos angle qui vont de -100° à 100° approximativement 
Serial.println(erreur);

        
        
 ///////////////////////////////////PID/////////////////////////////////////////// 

    
// on utilise l'axe X à implémenté dans le PID, ça veut dire que l'axe x de l'IMU est parrallele à la balance 
//on calcul d'abord l'erreur entre l'angle désiré (0°) et l'angle réel mesuré

erreur = angle_Total[1] - angle_souhaite;
// après viens la valeur du PID proportionnelle constante multiplié par l'erreur
pid_p=kp*erreur;

//puis viens la partie intégrale du PID qui ne doit marcher que si l'on est près de la valeur souhaité 
// c'est pourquoi il y a ajout d'une fonction if pour que la partie intégrale ne marche que si l'on a une erreur entre -3 et 3 degrés

if(-3< erreur< 3)
{
  pid_i = pid_i+(ki*erreur);
}

/* la dernière partie est la dérivée, la partie dérivée acte sur la vitesse de l'erreur, 
 la vitesse est le nombre d'erreur qui est produit dans un certain temps divisé par ce temps 
 pour cela on peut utiliser la variable erreur_avant. On soustrait a cette valeur l'erreur actuel et divise le tout par le tempsPassé
 Finalement on multiplie le tout par la costante de derivation */

   pid_d = kd*((erreur-erreur_avant)/Tempspasse);
 // la valeur finale du PID est la sommes des trois parties
 PID = pid_p + pid_i + pid_d;

/* Nous savons que la valeur minimale du signal PWM est de 1000us et le max est de 2000. 
   Nous pouvons donc dire que les valeurs du PID peuvent osciller entre 1000us et -1000us car lorsque l'on est a 2000us, 
   on peut au maximum retirer 1000us et quand on est Ă  1000us on peut au maximum rajouter 1000us */
if(PID < -1000)
{
  PID=-1000;
}
if(PID > 1000)
{
  PID=1000;   
}

pwmGauche = vitesse + PID;
pwmDroit = vitesse - PID;

// finalement on calcule la largeur des valeurs PWM pour être sûr qu'elles ne vont pas dépasser les valeurs minimum et maximum
//droit 3 ESC2
if(pwmDroit < 1000)
{
  pwmDroit = 1000;
}

if(pwmDroit > 2000)
{
  pwmDroit = 2000;
}
Serial.println(pwmDroit); 
// gauche 9 ESC

if(pwmGauche < 1000)
{
  pwmGauche = 1000;
}

 if(pwmGauche > 2000)
{
  pwmGauche = 2000;
}
Serial.println(pwmGauche);
// finalement on utilise la fonction servo pour créer les impulsions sur le PWM avec la largeur calculer de chaque impulsion


ESC.writeMicroseconds(pwmGauche);
//ESC2.writeMicroseconds(pwmDroit);
erreur_avant = erreur; // garde la derniere erreur 



}

Here is a photograph of my project:

What do you expect? The write to ESC2 is commented out!

Steve

Don't do that.

yes but actually i just forgot to put it back, i tried it without it being in comment and it didn't work out. My bad, sorry

why not ? i want it to be 0 but if i just put 0 it will never work out so i tried this, it s a like an hysteresis no ?

Because you need two comparison expressions.

The way you have it, the expression is evaluated left to right.

(-3< erreur< 3)

So, is erreur greater than -3?
The result is either true (aka 1) or false (aka 0).
Now is zero or one less than 3?

Sure is.
Always.

You need two comparisons, and the logical AND operator &&

i tried with this :

if(-3< erreur)
{if (erreur<3)
  {pid_i = pid_i+(ki*erreur);
}
}

is it how i should do it ?

Like I already told you

ok i changed it to

if (-3< erreur && erreur<3)

i repost the code :

#include <Servo.h>
#include <Wire.h>

Servo ESC;     // create servo object to control the ESC
Servo ESC2;


int16_t Acc_bruteX, Acc_bruteY, Acc_bruteZ, Gyr_bruteX, Gyr_bruteY, Gyr_bruteZ;

float angle_Acceleration[2];
float Angle_Gyro[2];
float angle_Total[2];

float Tempspasse, time, tempsPreced;
int i;
float rad_a_deg = 180/3.141592654;

float PID, pwmGauche, pwmDroit, erreur, erreur_avant;
float pid_p = 0;
float pid_i = 0;
float pid_d = 0;

////////////PID constantes////////////////////////////////////////
float kp = 3.55;
float ki = 0.005;
float kd = 2.05;
//////////////////////////////////////////////////////////////////

float vitesse=1300;// valeur de la vitesse de tour des moteurs
float vitesse2=1300;
float angle_souhaite = 0; // on veut que la balance reste parrallele au sol


void setup()  {
  Wire.begin(); //commence la communication par fil
  Wire.beginTransmission(0x68);  //envoi l'addresse esclave (0x68)
  Wire.write(0x6B); // accéder au registre 6B qui s'occupe du management de la force des moteurs
  Wire.write(0);
  Wire.endTransmission(true);
  Serial.begin(115200);
  ESC.attach(9); // (on attache l'ESC du moteur 1 a la pin 9)
  ESC2.attach(3);  // (on attache l'ESC du moteur 2 a la pin 3)

  time=millis(); // compte le temps en milliseconde
  //pour que les ESC démarre  nous avons besoin d'une valeur minimum qui est 1000us et et une valeur max de 2000us
  ESC.writeMicroseconds(1000);
  ESC2.writeMicroseconds(1000);
  delay(500); // donne du temps pour tout connecter et une dernière verification avant que tout commence. 
}


void loop(){

///////////////////////////////IMU/////////////////////////////////////////


   tempsPreced = time; // le temps précédant est enregistré et gardé avant que le vrai temps soit lu
   time = millis(); // temps actuel Ă  lire 
   Tempspasse=(time - tempsPreced)/1000;
// le Tempspassé est le temps passé depuis la dernière boucle,on divise par 1000 pour obtenir des secondes depuis les millisecondes 

 Wire.beginTransmission(0x68);
 Wire.write(0x3b); // demande le registre 0x3b et répond avec l'accélération selon x
 Wire.endTransmission(false);
 Wire.requestFrom(0x68,6,true); // l'appareil maitre demande Ă  l'appareil esclave 
                                //(MPU6050) les 6 bytes qu'il détient (accélération et gyro)

 Acc_bruteX=Wire.read()<<8|Wire.read(); // chaqu'une des valeurs Ă  besoin de deux registres
 Acc_bruteY=Wire.read()<<8|Wire.read();  
 Acc_bruteZ=Wire.read()<<8|Wire.read();  

// on calcul les angles avec l'equation d'Euler 
//la datasheet de la MPU6050 nous donne la valeur 16384.0 qui doit diviser nos valeurs pour nous donner des valeurs en g
//pour obtenir des degrées nous utilisons notre variable rad_a_deg
// on applique la formule d'euler
/*---X---*/
angle_Acceleration[0] = atan((Acc_bruteY/16384.0)/sqrt(pow((Acc_bruteX/16384.0),2)) + pow((Acc_bruteZ/16384.0),2))*rad_a_deg;
/*---Y---*/
angle_Acceleration[1] = atan(-1*(Acc_bruteX/16384.0)/sqrt(pow((Acc_bruteY/16384.0),2)) + pow((Acc_bruteZ/16384.0),2))*rad_a_deg;

// on fait maintenant la mĂŞme chose pour les valeurs du Gyro,
// les valeurs du Gyro commence Ă  0x43 et nous avons que 4 valeurs (pas besoin du Z, l'axe de lacet)

 Wire.beginTransmission(0x68);
 Wire.write(0x43); //addresse des valeurs du gyro
 Wire.endTransmission(false);
 Wire.requestFrom(0x68,4,true); //on a besoin de juste 4 valeurs 
   
 Gyr_bruteX=Wire.read()<<8|Wire.read(); // encore une fois on déplace les valeurs 8 fois vers la gauche 
 Gyr_bruteY=Wire.read()<<8|Wire.read();


// maintenant pour obtenir les valeurs du gyro il faut diviser par 131 parce que c'est le nombre que nous donne la datasheet

 /*---X---*/
Angle_Gyro[0]=Gyr_bruteX/131.0;
/*---Y---*/
Angle_Gyro[1]=Gyr_bruteY/131.0;

//maintenant pour obtenir des degrés, on doit multiplier ces degrés/secondes par le Tempspassé
//on peut donc apppliqué le dernier filtre

/*--- angle axe X---*/
angle_Total[0]=0.98*(angle_Total[0]+ Angle_Gyro[0]*Tempspasse)+ 0.02*angle_Acceleration[0];
/*--- angle axe Y---*/
angle_Total[1]=0.98*(angle_Total[1]+ Angle_Gyro[1]*Tempspasse)+ 0.02*angle_Acceleration[1]; 
// on a maintenant nos angle qui vont de -100° à 100° approximativement 
Serial.println(angle_Total[1]);
        
        
 ///////////////////////////////////PID/////////////////////////////////////////// 

    
// on utilise l'axe X à implémenté dans le PID, ça veut dire que l'axe x de l'IMU est parrallele à la balance 
//on calcul d'abord l'erreur entre l'angle désiré (0°) et l'angle réel mesuré

erreur = angle_Total[0] - angle_souhaite;
// après viens la valeur du PID proportionnelle constante multiplié par l'erreur
pid_p=kp*erreur;

//puis viens la partie intégrale du PID qui ne doit marcher que si l'on est près de la valeur souhaité 
// c'est pourquoi il y a ajout d'une fonction if pour que la partie intégrale ne marche que si l'on a une erreur entre -3 et 3 degrés

if (-3< erreur && erreur<3)
  {
    pid_i = pid_i+(ki*erreur);
}


/* la dernière partie est la dérivée, la partie dérivée acte sur la vitesse de l'erreur, 
 la vitesse est le nombre d'erreur qui est produit dans un certain temps divisé par ce temps 
 pour cela on peut utiliser la variable erreur_avant. On soustrait a cette valeur l'erreur actuel et divise le tout par le tempsPassé
 Finalement on multiplie le tout par la costante de derivation */

   pid_d = kd*((erreur-erreur_avant)/Tempspasse);
 // la valeur finale du PID est la sommes des trois parties
 PID = pid_p + pid_i + pid_d;

/* Nous savons que la valeur minimale du signal PWM est de 1000us et le max est de 2000. 
   Nous pouvons donc dire que les valeurs du PID peuvent osciller entre 1000us et -1000us car lorsque l'on est a 2000us, 
   on peut au maximum retirer 1000us et quand on est Ă  1000us on peut au maximum rajouter 1000us */
if(PID < -1000)
{
  PID=-1000;
}
if(PID > 1000)
{
  PID=1000;   
}

pwmGauche = vitesse + PID;
pwmDroit = vitesse2 - PID;

// finalement on calcule la largeur des valeurs PWM pour être sûr qu'elles ne vont pas dépasser les valeurs minimum et maximum
//droit 
if(pwmDroit < 1000)
{
  pwmDroit = 1000;
}

if(pwmDroit > 2000)
{
  pwmDroit = 2000;
}

// gauche 

if(pwmGauche < 1000)
{
  pwmGauche = 1000;
}

if(pwmGauche > 2000)
{
  pwmGauche = 2000;
}

// finalement on utilise la fonction servo pour créer les impulsions sur le PWM avec la largeur calculer de chaque impulsion*/


ESC.writeMicroseconds(pwmGauche);
ESC2.writeMicroseconds(pwmDroit);
erreur_avant = erreur; // garde la derniere erreur 
}

this is what i have actually

And . . ?

Only one of the motors worked only for error from 90 to 0 (so it’s better thank you)
But i still don t know why one of the motors does not work

The code would be more readable if you use a consistent naming convention:

  PID = pid_p + pid_i + pid_d;

  /* Nous savons que la valeur minimale du signal PWM est de 1000us et le max est de 2000.
     Nous pouvons donc dire que les valeurs du PID peuvent osciller entre 1000us et -1000us car lorsque l'on est a 2000us,
     on peut au maximum retirer 1000us et quand on est Ă  1000us on peut au maximum rajouter 1000us */
  PID = constrain(PID, -1000, 1000);

  pwmGauche = vitesseGuche + PID;
  pwmDroit = vitesseDroit - PID;

  // finalement on calcule la largeur des valeurs PWM pour être sûr qu'elles ne vont pas dépasser les valeurs minimum et maximum

  pwmDroit = constrain(pwmDroit, 1000, 2000);
  pwmGauche = constrain(pwmGauche, 1000, 2000);
  
  // finalement on utilise la fonction servo pour créer les impulsions sur le PWM avec la largeur calculer de chaque impulsion*/

  ESCGauche.writeMicroseconds(pwmGauche);
  ESCDroit.writeMicroseconds(pwmDroit);
  
  erreur_avant = erreur; // garde la derniere erreur
}

Not that a single 'constrain()' call can replace eight lines of 'if' statements.

Hi,

Have you swapped motors over to make sure both of them work?

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

Thanks.. Tom... :grinning: :+1: :coffee: :australia:

thank, i will change it right away, i didn't know about constrain()
it sure make it more readable.

Hi,
Can I suggest if you have no success you start with a clean slate and write code JUST to control the motors, before adding the IMU unit.

Prove you have motor control before trying to get the IMU to do it.

Then write some code JUST for the IMU to prove you can read valid data from it.

Tom... :grinning: :+1: :coffee: :australia:

i did and both of them are working


i tried to draw the circuit

i did it already

#include <Servo.h>
#include <Wire.h>

Servo ESC;     // create servo object to control the ESC
Servo ESC2;


int pulseMin = 1000;

int pulseMax = 2000;

int angleMax = 180;

long accelX, accelY, accelZ;

float gForceX, gForceY, gForceZ;

long gyroX, gyroY, gyroZ;

float rotX, rotY, rotZ;



void setup()

{

        Serial.begin(115200);

        // Attach the ESC on pin 9

        ESC.attach(9,pulseMin, pulseMax); // (pin, min pulse width, max pulse width in microseconds)
        ESC2.attach(3,pulseMin, pulseMax);    

        Serial.begin(74880);
         Wire.begin();
        setupMPU();                                                                      

}



void loop()

{

        for (int angleValeur = 0; angleValeur <= angleMax; angleValeur +=1)  // Tout les 1 degrés

        {

               
                ESC.write(angleValeur);
                ESC2.write(angleValeur);
                recordAccelRegisters();
                recordGyroRegisters();
                printData();

                delay(100);



        }
}

       
void setupMPU(){
  Wire.beginTransmission(0b1101000); //This is the I2C address of the MPU (b1101000/b1101001 for AC0 low/high datasheet sec. 9.2)
  Wire.write(0x6B); //Accessing the register 6B - Power Management (Sec. 4.28)
  Wire.write(0b00000000); //Setting SLEEP register to 0. (Required; see Note on p. 9)
  Wire.endTransmission();  
  Wire.beginTransmission(0b1101000); //I2C address of the MPU
  Wire.write(0x1B); //Accessing the register 1B - Gyroscope Configuration (Sec. 4.4)
  Wire.write(0x00000000); //Setting the gyro to full scale +/- 250deg./s
  Wire.endTransmission();
  Wire.beginTransmission(0b1101000); //I2C address of the MPU
  Wire.write(0x1C); //Accessing the register 1C - Acccelerometer Configuration (Sec. 4.5)
  Wire.write(0b00000000); //Setting the accel to +/- 2g
  Wire.endTransmission();
}

void recordAccelRegisters() {
  Wire.beginTransmission(0b1101000); //I2C address of the MPU
  Wire.write(0x3B); //Starting register for Accel Readings
  Wire.endTransmission();
  Wire.requestFrom(0b1101000,6); //Request Accel Registers (3B - 40)
  while(Wire.available() < 6);
  accelX = Wire.read()<<8|Wire.read(); //Store first two bytes into accelX
  accelY = Wire.read()<<8|Wire.read(); //Store middle two bytes into accelY
  accelZ = Wire.read()<<8|Wire.read(); //Store last two bytes into accelZ
  processAccelData();
}

void processAccelData(){
  gForceX = accelX / 16384.0;
  gForceY = accelY / 16384.0;
  gForceZ = accelZ / 16384.0;
}

void recordGyroRegisters() {
  Wire.beginTransmission(0b1101000); //I2C address of the MPU
  Wire.write(0x43); //Starting register for Gyro Readings
  Wire.endTransmission();
  Wire.requestFrom(0b1101000,6); //Request Gyro Registers (43 - 48)
  while(Wire.available() < 6);
  gyroX = Wire.read()<<8|Wire.read(); //Store first two bytes into accelX
  gyroY = Wire.read()<<8|Wire.read(); //Store middle two bytes into accelY
  gyroZ = Wire.read()<<8|Wire.read(); //Store last two bytes into accelZ
  processGyroData();
}

void processGyroData() {
  rotX = gyroX / 131.0;
  rotY = gyroY / 131.0;
  rotZ = gyroZ / 131.0;
}

void printData() {
  Serial.print("Gyro (deg)");
  Serial.print(" X=");
  Serial.print(rotX);
  Serial.print(" Y=");
  Serial.print(rotY);
  Serial.print(" Z=");
  Serial.print(rotZ);
  Serial.print(" Accel (g)");
  Serial.print(" X=");
  Serial.print(gForceX);
  Serial.print(" Y=");
  Serial.print(gForceY);
  Serial.print(" Z=");
  Serial.println(gForceZ);
}

this code has the code of the MPU and the code of the motors but they are not linked

Hi,
Try this code, it brings the ESCs up to speed then back down one after the other.
Monitor the IDE serial monitor with a baud rate of 115200.

#include <Servo.h>

Servo ESC1;     // create servo object to control the ESC
Servo ESC2;

int pulseMin = 1000;

int pulseMax = 2000;

int angleMax = 180;

void setup()

{
  Serial.begin(115200);
  ESC1.write(0);
  ESC2.write(0);
  ESC1.attach(9);
  ESC2.attach(3);
  delay(500);
  Serial.println("STARTING");
}

void loop()
{
  for (int angleValeur = 0; angleValeur <= 180; angleValeur += 1) //Throttle up ESC1
  {
    ESC1.write(angleValeur);
    //    ESC2.write(angleValeur);
    Serial.print("ESC1 THROTTLE ANGLE = ");
    Serial.println(angleValeur);
    delay(100);
  }
  for (int angleValeur = 180; angleValeur >= 0; angleValeur -= 1) //Throttle down ESC1
  {
    ESC1.write(angleValeur);
    //    ESC2.write(angleValeur);
    Serial.print("ESC1 THROTTLE ANGLE = ");
    Serial.println(angleValeur);
    delay(100);
  }

  for (int angleValeur = 0; angleValeur <= 180; angleValeur += 1) //Throttle up ESC2
  {
    //    ESC1.write(angleValeur);
    ESC2.write(angleValeur);
    Serial.print("ESC2 THROTTLE ANGLE = ");
    Serial.println(angleValeur);
    delay(100);
  }
  for (int angleValeur = 180; angleValeur >= 0; angleValeur -= 1) //Throttle down ESC2
  {
    //    ESC1.write(angleValeur);
    ESC2.write(angleValeur);
    Serial.print("ESC2 THROTTLE ANGLE = ");
    Serial.println(angleValeur);
    delay(100);
  }
  Serial.println("ALL STOP FOR 2 SECONDS.");
  delay(2000);
}

Tom... :grinning: :+1: :coffee: :australia:

hello
with this code one is just shaking and the shatft is not rotating and the other one doesn t work at all.