Arduino + Speed Controller PPM with Interrupts PROBLEM (Video Attached)

Hello all

First of all, I have done quite a bit of Googling on this problem already. Most issues I have seen on these forums involve READING the pulses I am trying to create. I, on the other hand, am basically trying to reverse engineer an RC receiver.

Ok so here is my setup and the problem:

I am trying to control a Turnigy ESC which is powering a Turnigy brushless motor used for RC planes etc. The ESC, from what I've been able to discover, uses Pulse Position Modulation (PPM for short). The pulses arrive every 20ms, and the width of the pulse determines the throttle range. The pulse width varies between 1ms and 2ms.

I have set up my Arduino to output a signal such as this. It works fairly well so far - I can successfully arm the ESC using a 1ms pulse and can cause the motor to spin. The problem is, the motor will intermittantly slow down and speed up as if the pulses were missed. Here is a video to show what I'm talking about:

I am using an interrupt approach in my code. I also tried a basic loop within the loop() function which does the same thing and has the same problem.

#include <TimerOne.h>

int pulseCounter = 0;
int frameCounter = 0;
int pulseLength = 100;
int pulseMode = 0;

void setup()
{
  pinMode(10, OUTPUT);
  Timer1.initialize(10);         // initialize timer1, and set a 1/2 second period
  Timer1.attachInterrupt(frameISR);  // attaches callback() as a timer overflow interrupt
  digitalWrite(10, LOW);
  Serial.begin(9600);
}

//Fires every 10 microseconds
void frameISR()
{
  frameCounter++;
  pulseCounter++;
  
  if (pulseMode == 0)
  {
    if (frameCounter >= 2025)
    {
      //Serial.println("Frame start");
      digitalWrite(10, HIGH);
      pulseMode = 1;
      frameCounter = 0;
      pulseCounter = 0;
    }
  }
  else
  {
     if (pulseCounter >= pulseLength)
     {
       //Serial.println("Pulse End");
       digitalWrite(10, LOW);
       pulseMode = 0;
       pulseCounter = 0;
     }
  }
}


void loop()
{
  if (Serial.available() > 0) {
  	pulseLength = map(Serial.read(),48, 57, 100, 200);
  }
  
  Serial.print("Current pulseLength: ");
  Serial.println(pulseLength);
}

Basically I set up an interrupt to fire every 10 microseconds. Ever 2000*10 microseconds (20ms) I set the pin HIGH. I have a second counter to count the pulse length (between 1ms to 2ms) after which the pin is set LOW.

Any ideas on what could be causing this problem?

Thanks

I suspect this might be do-able with the servo library since in my understanding its kind of what its for.

My other thought would be to declare pulseMode, frameCounter and pulseCounter as volatile since they are being modified in the ISR, eg

volatile int pulseMode = 0;

I tried the servo library but I have a few problems with it. Also, for what i'm using it for, I'd like to make my own library.

I will try the volatile keyword, I hadn't thought of that.

On second thought, I think your problems go a bit deeper.

Your overflow interrupt is firing every .00001 seconds or so. I assume you are running 16MHz which gives you about 160 clock cycles per overflow.

digitalWrite takes about 53 clock cycles to complete, I'm not going to to go through your ISR but you are probably looking at 70%+ processor utilization just trying to count that often. I'm guessing its that you are running the interrupt so often combined with attempting serial communication causing problems.

I would try adding the volatile keywords, changing the digitalWrites to directly flipping that pins bit in the PORT register, if you are using an UNO:

PORTB |= 1<<PB2; //set 10 high
PORTB &= ~(1<<PB2); //set 10 low

And finally, serial printing from an ISR just strikes me as a bad idea in general but it looks like you might have figured that out.

Yup I knew print is a bad idea, that was just to make sure my logic was working.

I did not know how to set those pins directly....I assumed that digitalWrite would be fairly quick. Wrong assumption I guess. I'll try this and get back.

Unfortunately, I need that refresh rate on the ISR in order to have sufficient granularity for the width of the pulse. I'm hoping I have enough room for everything else later but for now I'm just focusing on motor control

You may be able to achieve that granularity by having the pulse width be directly controlled by timer1, pwm gives you good control over the width of pulses, the trick will be triggering one pulse every 20ms.

I think you can just use timer 1, put it in phase and frequency correct pwm mode such that the entire period is 20ms and then keep your pulse width between 1 and 2 ms. Put PWM in set on count up mode. This should work without interrupts at all.

That library won't do it, I've played with it before and your pwm output function is only 10-bits and you will need all 16 to get this working right.

void ESCinit(int length) { //run once, length same meaning as below.
        ICR1 = 20000;
        TCCR1A = 0b00110000;
        TCCR1B = 0b00010010;
        ESCPulseSet(length);
        DDRD |= 1<<PB2;
}

int ESCPulseSet(int length) { // pulses once for 1000 + length us every 20 ms.
       OCR1B = 19000 - length;
}

Try those if you want. I'd be surprised if it just worked but you never know!

Is there a way to change the voltage of the output pins? Right now i'm reading about 0.5volts out of pin 10. I'm not sure what voltage the ESC is designed to read, but I would have thought it to be in the 3.3-5v range.

What are you reading the voltage with? I just edited in some routines that you might want to try, I don't have a scope or anyway to actually verify that output at the moment but that should be interrupt free pulse with us granularity every 20 ms.

You can't change the output voltage, you are just likely measuring short pulses with something that can't keep up.

Actually your video sounds pretty good to me. Keep in mind that the motor is being run 'unloaded', with a proper sized load (prop) it might smooth out to more of what ever your expections might be.

You should be using the servo library to control the ESC. An ESC utilizes a servo input signal directly, it's normally driven by just one of the several servo outputs of a typical R/C receiver. Not saying you can't generate your PPM output in your sketch, but the servo library gives you a tested piece of software that is bound to be less effected by other tasks your sketch might need to perform.

As far as your voltage question, I'm not clear what you are asking about. A PPM is a digital signal, it is either at a valid digital HIGH value or a valid digital LOW voltage at any given instant in time just like as shown on your scope display. As R/C servo systems are designed to run at 5vdc just like a arduino board, you should have no issues with voltage levels.

Here is a link to the arduino servo library commands. You would use the writeMicroseconds(xxxx); command where xxxx = 1000 for full stop and 2000 for 100% speed.

Lefty

Actually your video sounds pretty good to me. Keep in mind that the motor is being run 'unloaded', with a proper sized load (prop) it might smooth out to more of what ever your expections might be.

For people who may have the same problems as me in the future:

Initially I shied away from the servo library for a number of reasons, One of which was because I was having the intermittent speed issue described previously.

However, as the poster above me suggested, the motors smoothed out GREATLY when they were loaded. This turned out to be the biggest problem and now that I have propellers on it, things are working much better.

I have also moved back to using the servo library, which works great.

Thanks for your help guys.

Hello again

So these controllers have been giving me nothing but trouble all week.

I implemented an alternative control program using the Servo library as suggested in this thread. Unfortunately, for some reason, neither this new code or my interrupt code in the OP seem to be able to even arm the ESC's let alone drive them now. I'm not sure what the problem is since it WAS working.

I'm starting to think there is some sort of hardware problem but I can't think of what it could be. I have hooked this new code up to the oscilloscope and it looks fine. Also, the ESC's work 100% fine when I plug them into my RC receiver and use my radio to control them directly. (I didn't have a radio to try this out last week)

One thing I did notice, however, was that the PPM signal from the receiver has a frame width of 16ms vs the 20ms in the servo library. I changed my interrupt code to use a 16ms frame but it didn't help. (My interrupt code from the OP was indistinguishable from the receiver signal output)

Here's the new code:

#include <Servo.h>

Servo motorBackLeft;
Servo motorBackRight;
Servo motorFrontLeft;
Servo motorFrontRight;

int pulseWidth = 0;

void setup()
{
  motorFrontLeft.attach(4, 1000, 2000);
  motorFrontRight.attach(5, 1000, 2000);
  motorBackLeft.attach(2, 1000, 2000);
  motorBackRight.attach(3, 1000, 2000);
  
  motorBackLeft.writeMicroseconds(pulseWidth);
  motorBackRight.writeMicroseconds(pulseWidth);
  motorFrontLeft.writeMicroseconds(pulseWidth);
  motorFrontRight.writeMicroseconds(pulseWidth);
  
  Serial.begin(9600);
}

void loop()
{
  if (Serial.available() > 0) {
    pulseWidth = map(Serial.read(),48, 57, 1000, 2000);
  }
  
  motorBackLeft.writeMicroseconds(pulseWidth);
  motorBackRight.writeMicroseconds(pulseWidth);
  motorFrontLeft.writeMicroseconds(pulseWidth);
  motorFrontRight.writeMicroseconds(pulseWidth);
  
  Serial.println(pulseWidth);
}

And here's a picture of the middle of my quadrocopter

Any ideas? I'm getting a bit desperate here

In your 'new' code, I don't see any attempt to send the proper sequence of servo commands to 'arm' the ESCs. Usually these kind of ESCs won't start motor control until first 'armed' with specific squence as defined by the manufacture. One I had you had to send a 100% speed command for one second or more, then a 0% speed command for one second or more and only then would valid servo commands be processed to control motor speed. I think it also issued a sound when arming was valid and complete.

Also I see you have a Serial.println(pulseWidth); at the end of your loop function, what values are you getting back in the serial monitor ? are the pulseWidth values displayed in the serial monitor always in the 1000-2000 valid range?

Lefty

Ah yeah I should have mentioned that.

The function in the loops takes serial input from a console. I use the ASCII characters 0-9 and map the input to a pulse width 1000-2000 usec.

So to arm the motors, i run the code, type '0' in console and then power the ESC's. After they have armed, I can hit '9' for max speed.

That's how I arm the motors and control the throttle

Oh and I confirm the arduino output using an oscilloscope. It definitely outputs in that range.