Twitching Burshless Motor When RC (PWM) Servo Input Given Through Microprocessor

Hi there,
Zloyhan'18
Please I wish urgent help.

I am currently working on an RC project with fixed-wing aircraft. The goal of the project is to use an esp-32-based microprocessor (it is not esp32, a card named deneyap) to apply some flight controls to the system via an Autopilot / RC input. Basically a homemade autopilot. We will use this in an contest upcoming.

The current state of the project:

  • I have two different power sources: USB and 3s 25c lipo (11.1v - 12.4v)
  • There are 1 Reciever (Rx), 1 Radio controller (fly sky i6x with 10 ch, Tx), 4 servo motors (tower pro sg90, 180deg) 1 ESC (brandless 30A), 1 brushless motor (brandless 1400KV). and some sensors with an I2C connection.

I am basically taking ppm signal from Rx, and analysing its signals in interrupts, dividing it into 8 channel inputs with microseconds.

Here you can see the whole wiring scheme I created. I only had paint in my hand so please excuse me for not having any technical electrical scheme.

Basically, the lipo powers the Rx, Servos, ESC, and Brushless motor; while the signal cables are connected to the microprocessor which is powered by the USB cable. I read and write data for debugging and testing from console commands.

I then do not edit the data, I basically re-write the channel inputs only reversing if necessary. I create PWM signals 250hz for all of the components.

Here is my whole code with explanations:

// LIBRARIES USED
  #include "deneyap.h"
  #include <Wire.h>

//PIN SETTINGS
  const int BZRpin = D0; //inout pin for buzzer
  const int PPMpin = D1; //pinout for ppm
  const int RightAileronPin = D12;
  const int LeftAileronPin = D13;
  const int ElevatorPin = D14;
  const int ThrottlePin = D4;
  const int RudderPin = D15;

//CHANNEL ASSIGNMENTS
  const int RightAileronCH = 1;
  const int LeftAileronCH = 1; // must be negative value (reversed)
  const int ElevatorCH = 2;
  const int ThrottleCH = 3;
  const int RudderCH = 4;

//RC INPUT SETTINGS
  const int channelNum = 8; //num of channels inputed - PUBLIC

//PPM SETTINGS
  volatile long PW[channelNum] = {0}; //        array of pulse-width of channels in micro sec PRIVATE
  volatile long ptime = 0; //                                specific pulse time in micro sec PRIVATE
  volatile long crtime = 0; //                                      current time in micro sec PRIVATE
  volatile int pulse = 0; //                                        num of encountered pulses PRIVATE

//RC OUTPUT SETTINGS
  float ThrottleInput;
  float RightAileronInput;
  float LeftAileronInput;
  float ElevatorInput;
  float RudderInput;
  float TestServoInput;

//MPU SETTINGS
  float RateROLL, RatePITCH, RateYAW;
  float ROLLcal, PITCHcal, YAWcal;
  int NUMcal; // private
  const int NUMtotalcal = 4000;

// MAIN METHOD
void setup(){
  Serial.begin(57600); // initlize serial com

  pinMode(BZRpin, OUTPUT); // buzzer initiated

  pinMode(RightAileronPin, OUTPUT);   // pins for servos for Right Aileron initiated
  pinMode(LeftAileronPin, OUTPUT);    // pins for servos for Left Aileroninitiated
  pinMode(ElevatorPin, OUTPUT);       // pins for servos for Elevator initiated
  pinMode(RudderPin, OUTPUT);         // pins for servos for Rudder initiated
  pinMode(ThrottlePin,OUTPUT);        // pins for ESC for Throttle initiated
  
  Serial.print("Please wait...");

  //PWM setup
  analogWriteFrequency(250);
  analogWriteResolution(12);
  delay(250);

  //Set control surfaces to safe oriantation for pre-flight
  analogWrite(RightAileronPin,1.024*1500);
  analogWrite(LeftAileronPin,1.024*1500);
  analogWrite(ElevatorPin,1.024*1500);
  analogWrite(RudderPin,1.024*1500);
  delay(2000);

  //system message on power
  Serial.println(" ");
  Serial.println("SYSTEM POWERED");
  Serial.println("==============");
  buzzersig(200);
  buzzersig(200);
  delay(1000);

  //--------------- CONFIGURATIONS -----------------//
    Serial.println("- Configuring settings -");
    delay(500);

    Serial.print("MPU (I2C Settings)..."); // Settings for MPU I2C Com.
    
      Wire.setClock(400000); //I2C 
      Wire.begin();
      delay(250);
      Wire.beginTransmission(0x68);
      Wire.write(0x6B);
      Wire.write(0x00);
      Wire.endTransmission();
    
      Serial.println("DONE");
    delay(500);

    /*
    Serial.print("Calibrqation 2")        //desc
      
      //cal

      Serial.println("DONE");
    delay(500);
    */

    Serial.println("SUCCESS!");
    buzzersig(200);
    Serial.println(" ");
    delay(500);

  //---------------- CALIBRATIONS ------------------//
    Serial.println("- Sensor Calibrations - (INITIATING)!");
    delay(500);

    Serial.print("Calculating IMU - Do not touch the platform!");
      delay(2000);
      buzzersig(500);

      for(NUMcal = 0; NUMcal < NUMtotalcal; NUMcal++) {
        gyro_sig();
        ROLLcal+= RateROLL;
        PITCHcal+= RatePITCH;
        YAWcal+= RateYAW;
        delay(1);
        if(NUMcal%1000 == 0 ) {
          Serial.print(".");
          buzzersig(100);
        }
      }
      ROLLcal/=NUMtotalcal;
      PITCHcal/=NUMtotalcal;
      YAWcal/=NUMtotalcal;

      Serial.println("DONE");
    delay(500);

    /*
    Serial.print("Calculating X - Do not touch the platform!");
      delay(2000);
      buzzersig(500);

      cal 2

      Serial.println("DONE");
    delay(500);
    */

    Serial.println("SUCCESS!");
    buzzersig(200);
    Serial.println(" ");
    delay(500);
  //system message on succesful Initialization
  Serial.println(" ");
  Serial.println("SYSTEMS INITIATED");
  Serial.println("================");
  buzzersig(200);
  buzzersig(200);
  buzzersig(200);

  //Inıtiated PPM Reading functions
  delay(500);
  attachInterrupt(digitalPinToInterrupt(PPMpin), ppmread, FALLING); // Attach interrupt to pin for reading PPM channels.
  Serial.println(" ");
  Serial.println("Waiting for Tx Signal! (CH1 is sampling)");

  while(PW[0] < 900 || PW[0] > 2100) {
    delay(1000);
    Serial.print(".");
    Serial.print(PW[0]);
    delay(1000);
  }

  Serial.println(" ");
  Serial.println("CONNECTED!");
  buzzersig(200);

  //safety for microporcessor resets 
  while(PW[ThrottleCH-1] > 1020  ) {
    delay(4);

    Serial.println("ATTENTION!! LOWER THE THROTTLE STICK!!");
    Serial.print(PW[ThrottleCH-1]);
    Serial.print("[/µs] ");

    Serial.println();
    buzzersig(200);
    //delay(200);
  }

  //electronically starting the burshless motors
  /*
  analogWriteFrequency(250);
  buzzersig(200);
  analogWrite(ThrottlePin,1.024*1000);
  delay(200);
  analogWrite(ThrottlePin,1.024*2000);
  delay(200);
  analogWrite(ThrottlePin,1.024*1000);
  delay(2000);
  */
}

void loop() {

  gyro_sig(); // get gyro data and apply calibration
    RateROLL-=ROLLcal;
    RatePITCH-=PITCHcal;
    RateYAW-=YAWcal;
  /*
    Serial.print("ROLL r: ");
    Serial.print(RateROLL);
    Serial.print(" PITCH r: ");
    Serial.print(RatePITCH);
    Serial.print(" YAW r: ");
    Serial.println(RateYAW);
    delay(50);
    /*
    7*
    for (int i =0 ; i<channelNum; i++) {
      Serial.print("Ch");
      Serial.print(i+1);
      Serial.print(": ");
      if(i==7)
      {
        Serial.println(PW[i]);
      }
      else
      {
        Serial.print(PW[i]);
        Serial.print("  ");
      }
      
    }
  */
  //write throttle to ESC
  if(PW[ThrottleCH-1] < 1030) 
  { //prevents twitching
    ThrottleInput = 1.024*1000;
    analogWrite(ThrottlePin, (ThrottleInput));
  }
  else 
  { //applies throttle    
    ThrottleInput = 1.024*PW[ThrottleCH-1];
    analogWrite(ThrottlePin, (ThrottleInput));
  }

  //write Right Aileron
    RightAileronInput = 1.024*PW[RightAileronCH-1];
    analogWrite(RightAileronPin, (RightAileronInput));

  //write Left Aileron
    LeftAileronInput = 1.024*map(PW[LeftAileronCH-1],1000,2000,2000,1000);
    analogWrite(LeftAileronPin, (LeftAileronInput));

  //write Rudder
    RudderInput = 1.024*PW[RudderCH-1];
    analogWrite(RudderPin, (RudderInput));
    
  //write Elevator
    ElevatorInput = 1.024*PW[ElevatorCH-1];
    analogWrite(ElevatorPin, (ElevatorInput));

  /*
    Input = 1.024*PW[CH-1];
    analogWrite(Pin, (Input));
  */
    //prints out channel inputs for debugging
      for(int j=0; j<channelNum; j++) {
        Serial.print("CH");
        Serial.print(j+1);
        Serial.print(": ");
        if(j==7)
        {
          Serial.println(PW[j]);
        }
        else
        {
          Serial.print(PW[j]);
          Serial.print("  ");
        }
      }
      delay(4);
}

void ppmread() { //interrupt method

  switch (pulse) {
    default: 
      pulse++;
      ptime = micros();
    break;

    case 1:
      crtime = micros();
      PW[pulse-1] = crtime - ptime;
      pulse++;
      ptime = crtime;
      if (PW[pulse-2]>2100) {
        pulse = 1;
      }
    break;

    case 2:
      crtime = micros();
      PW[pulse-1] = crtime - ptime;
      pulse++;
      ptime = crtime;
      if (PW[pulse-2]>2100) {
        pulse = 1;
      } 
    break;

    case 3:
      crtime = micros();
      PW[pulse-1] = crtime - ptime;
      pulse++;
      ptime = crtime;
      if (PW[pulse-2]>2100) {
        pulse = 1;
      }
    break;

    case 4:
      crtime = micros();
      PW[pulse-1] = crtime - ptime;
      pulse++;
      ptime = crtime;
      if (PW[pulse-2]>2100) {
        pulse = 1;
      }
    break;

    case 5:
      crtime = micros();
      PW[pulse-1] = crtime - ptime;
      pulse++;
      ptime = crtime;
      if (PW[pulse-2]>2100) {
        pulse = 1;
      }
    break;

    case 6:
      crtime = micros();
      PW[pulse-1] = crtime - ptime;
      pulse++;
      ptime = crtime;
      if (PW[pulse-2]>2100) {
        pulse = 1;
      }
    break;

    case 7:
      crtime = micros();
      PW[pulse-1] = crtime - ptime;
      pulse++;
      ptime = crtime;
      if (PW[pulse-2]>2100) {
        pulse = 1;
      }
    break;

    case 8:
      crtime = micros();
      PW[pulse-1] = crtime - ptime;
      pulse = 0;
      ptime = crtime;
      if (PW[pulse+channelNum-1]>2100) {
        pulse = 1;
      }
    break;
  }
}

void buzzersig(int ms) {
    digitalWrite(BZRpin,HIGH);
    delay(ms);
    digitalWrite(BZRpin,LOW);
    delay(ms);
}

void gyro_sig() {
  Wire.beginTransmission(0x68);
  Wire.write(0x1A);
  Wire.write(0x05);
  Wire.endTransmission();

  Wire.beginTransmission(0x68);
  Wire.write(0x1B);
  Wire.write(0x8);
  Wire.endTransmission();

  Wire.beginTransmission(0x68);
  Wire.write(0x43);
  Wire.endTransmission();

  Wire.requestFrom(0x68,6);
  int16_t GyroX=Wire.read()<<8 | Wire.read();
  int16_t GyroY=Wire.read()<<8 | Wire.read();
  int16_t GyroZ=Wire.read()<<8 | Wire.read();

  RateROLL=(float)GyroX/65.5;
  RatePITCH=(float)GyroY/65.5;
  RateYAW=(float)GyroZ/65.5;
}
  

There are a few other codes for system initialization etc. Do not mind them.

The whole system works fine, BUT I have a persistent issue:
The problem I cannot solve is that whenever I start moving servos a bit fast, the brushless motors start "twitching" moving and spinning sometimes.

I have tried writing the channel values and the variable ( ThrottleInput float) I input for the esc in the form of a PWM signal. I see that I input the lowest input, BUT SOMEHOW motor still spins and "twitches" to this day.

Note for PPM signalling: 1000 microseconds (lowest, min input) for CH length in PPM pulse, I convert it in the code to a form that can be inputted as an arg in analogWrite, so I have a PWM wave at the end.

I have observed that:

  • Only motor experiences such behaviour. Not other servos.
  • More vigorously the servo channel input from the transmitter, Tx, (moving sticks faster to left and right) the spinning of the motor movement increases and becomes more significant.

I have read about servos exceeding 5v and creating voltage spikes (mine are 4.8 - 5.3 depending on the way I move them through Tx, I measured.) may be causing this problem.
I have talked with a professor from a university (a professional) and he stated that the problem could be caused by using the same ground. for signalling and powering.

I need the USB cable for reading data till I finish this project, I have ordered I PCB for a more streamlined 5V output (BEC integrated PDB). Do I need to power everything from lipo? I will be unable to use USB when the system is powered on, I do not want this.

Please I wish urgent help.

What does monitoring your PPM signal with an oscilloscope show?

I do not have access to any oscilloscope, But the signal should be something like this:

I got this from a PDF called "Notes on flysky i6" Here is the link if you want to access it.

I believe that I receive the signal correctly, when I print the channel values I calculated/measured, they are exactly what they ought to be.

And your project SHOULD not be having problems, but it is. So get the proper equipment to debug your project. You might also look into logic analyzers.

Thank you for your assist @Paul_KD7HB ,

So get the proper equipment to debug your project.

I believe there was a misunderstanding.
I do not OWN such equipment, that's what I was trying to say, I work from home for this project basically.

However, I can use one which is stationed at the school lab, or I know several people who can let me use one.

You might also look into logic analyzers.

I looked it up and probably acquire one for these projects. Thank you.

I am will be waiting for your further suggestions.

And I will wait for your test results.

Hi there, @Paul_KD7HB,

I have been trying to reach an oscilloscope for the last 2 days, it turns out that the school lab does not have a working oscilloscope, unfortunately. I will still try to reach one, however, isn't there anything else I can check or debug from somewhere else?

Anyway, I found out about something and wanted to ask you.

I investigated further and found out about Ferrite Rings / Nuve. They are consistently used in RC systems, which as I found out, are used to block excessive electromagnetic noise in signalling cables. Will that actually solve my main problem?

I make this suggestion minding that I assume that I have an analogue problem with the Voltage peaks of servos, signalling to esc etc.

kindly, Thank you!

Here's an example of what I saw, please correct me if needed:

Ferrite rings are only effective for a range of frequencies. The ferrite material is made in various mixes to have the most inductive reactance at certain frequencies. Without knowing the wave form of possible interference, picking the proper ferrite mix is just a guess.
The picture you gave show using a ferrite ring to stop "common mode" interference. That means all three of the wire are conducting the identical interfering signal. If only one wire has the interference, then the ferrite needs to be on ONLY that one wire.
But the picture makes me ask if your project has any signal wires that are close to any power wires? They should be far apart and only cross at right angles.

Actually, there are.

At the moment, I basically connect the lipo directly to ESC, which are very close to the ESC signal wire bundle.

Here's an image:

Also, white and black cable bundles are from Rx to servos which carry power too, I use the servo rail in the Rx to power the servos.

Blue wire bundle is the signal wires of servos,
The vague orange line coming from microprocessor to esc junction is the signal wire of the ESC.

Also, is there a way to deduce that? Maybe from the clock speed, or adding a delay() / sleep() etc. function to match the control loop hz. I can look up to datasheets also. my pwm signal is 250hz in the code for both ESC and servos.

Also, should I change the Hz of the PWM respectively to both kinds of components?

Also, what do you suggest for the separation of grounds for signals and power, when I first tried, it ended with all the components moving chaotically. This is one of the biggest questions in my head.

Thank you @Paul_KD7HB !

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