EngineRunIn (YASP  Yet Another Servo Program)

I fly RC and wanted to break in a new gas engine. Being gas it takes about 2 gallons of fuel to do a good break in. Wanting to automate this to running on a test bench so I talked with the electronics group on RCGroups.com and went with the Arduino board.
The program is a pseudo menu interface and functions can be stacked for a varied run sequence. Right now the function choices are; fullt(hrottle), idle, midt, center, menu, manual, stop; some take options. Manual drives the servo with the up down arrow keys. I’m using Putty for terminal interface ( my laptop is running Vista).


  1. Code isn’t LMC, I think it’s clumsy in many places and I’ve had an occasional glitch with passing options
  2. Interface is crude, I’m thinking of adapting ansiterm or sdfruit.
    3.Needs an interrupt routine to kill a run from the keyboard. I having trouble with this. Plan on also having an interrupt button at the Arduino; no racing for the PC in a panic.
    4.Need to have a better understanding of the UART interface. I can’t make the menu too verbose without the program locking.
    5.If I get this far I may start interfacing some data recording; an RPM interface would be great.
#define PROGRAM "EngineRunIn"
#define VERSION "0.9"
 * PROGRAM:    EngineRunIn (YASP  Yet Another Servo Program)
 * VERSION:    0.9 8/14/2010
 * DESCRIPTION:  Automate engine run-in program using command line arguements to vary throttle position 
 *                and duration.             
/* Throttle control */
#include <Servo.h>
#include <stdio.h>            /* we need fundamental FILE definitions and printf declarations */

#define SERVOPIN  2                                     /* pin used for servo */
#define PORTSPEED  38400                              /* Baud rate */

/* tune these 2 to fit your installation */
#define LOWT 45                                            /* Low throttle.  */
#define HIGHT 155                                      /* High throttle. */

#define NL "\r\n"                        
#define PROMPT  ">:"
#define IFS ' '                                           /* Internal field separator */
#define CS ';'                                           /* command separator */
#define BEL '\7'                                          /* Bell */

#define DEBUG 0                                           /* 1 on, 0 off */
#define DELAY 1000
#define OPT_DELIM '-'
/* Set up the serial port and servos. */
Servo Tservo;                                           /* create servo object  */
/* All the routines look at Lthrottle & Hthrottle
** Setup() routine can change Lthrottle & Hthrottle,
** used to determine travel setting for your installation.
** LOWT & HIGHT are then permanently changed
int Lthrottle = LOWT;
int Hthrottle = HIGHT;

int CPOS = Lthrottle;                              /* current servo position; tracked in every subroutine */
#define SERVOCENTER Hthrottle/2                    /* Center servo */

/*     cliBuildCommand    */
#define MAX_COMMAND_LEN             (32)  /* max # of chars */
#define MAX_PARAMETER_LEN           (32)
#define COMMAND_TABLE_SIZE          (32)  /* for command_t */
#define TO_UPPER(x) (((x >= 'a') && (x <= 'z')) ? ((x) - ('a' - 'A')) : (x))

char gCommandBuffer[MAX_COMMAND_LEN + 1];
char gParamBuffer[MAX_PARAMETER_LEN + 1];
char strBuf[COMMAND_TABLE_SIZE + 1];
char functionBuf[COMMAND_TABLE_SIZE + 1];
char optionBuf[COMMAND_TABLE_SIZE + 1];
int  gParamValue = false;

char *cmdptr = gCommandBuffer;
char *optptr = gParamBuffer; 
char *str = strBuf;
char *str1 = functionBuf;
char *str2 = optionBuf;

static FILE uartout = {0} ;      /* create a FILE structure to reference our UART output function */
/* create a output function; this works because Serial.write, 
** although of type virtual, already exists. 
static int uart_putchar (char c, FILE *stream)
{Serial.write(c) ;return 0 ;}
/* sample code practice:
** printf("Alive %d sec\n\r", seconds ) ;
** printf("%d  hello m%cworld\n", seconds,c);

typedef struct {
  char const    *name;
  void          (*function)(void);

command_t const gCommandTable[COMMAND_TABLE_SIZE] = {
  {"MENU",         menu,    },
  {"BLIP",         blip,     },
  {"B",            blip,     },  /* shortcut */
  {"CENTER",       centerservo,  },
  {"CNTR",         centerservo,  },
  {"CYCLE",        cycleservo,     }, /* For testing, NOT on menu, may not work */
  {"MIDT",         midt,     },
  {"FULLT",        fullt,     },
  {"IDLE",         idle,     },
  {"EXIT",         stopProgram,      },
  {"E",                stopProgram,     },/* shortcut */
  {"STOP",         stopProgram,     },
  {"S",                stopProgram,     },/* shortcut */
  {"QUIT",         stopProgram,     },
  {"Q",                stopProgram,     },/* shortcut */
  {"KILL",         stopProgram,     },
  {"K",                stopProgram,     },/* shortcut */
  {"HELP",         menu,                 },
  {"SETUP",        setupTravel,      },/* write this */
  {"MANUAL",       manualT,            },
  {"M",              manualT,            },/* shortcut */
  {NULL,           NULL             }

void setup() {
  Serial.begin(PORTSPEED); /* opens serial port, sets data rate */

   /* PRINTF STARTUP: fill in the UART file descriptor with pointer to writer. */
   fdev_setup_stream (&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE);
   stdout = &uartout ;            /* The uart is the standard output device STDOUT. */

  printf("%s%s Version: %s%s", NL, PROGRAM, VERSION, NL);


void loop() {
  char rcvChar;
  int  bCommandReady = false;

  if (Serial.available() > 0) {   
    rcvChar = Serial.read();                      /* Wait for a character. */
    Serial.print(rcvChar);                         /* Echo the character back to the serial port. */
    bCommandReady = BldCmds(rcvChar);              /* Build a new command. */

  /* Call the CLI command processing routine to verify the command entered */
  /* and call the command function; then output a new prompt. */
  if (bCommandReady == true) {
    bCommandReady = false;
  cmdptr = &gCommandBuffer[0];
  optptr = &gParamBuffer[0];

}/* END loop() */

int BldCmds(char nextChar) {
  static uint8_t idx = 0; //index for command buffer
  static uint8_t idx2 = 0; //index for parameter buffer
  enum { COMMAND, PARAM     };
  static uint8_t state = COMMAND;
  if ( (nextChar == ' ') || (nextChar == '\t')) /* skip space & tab */
    return false;
if( (nextChar == 'k') && (idx == 0) )
      printf("GOT A K\n");
  if ((nextChar == '\n') || (nextChar == '\r')) /* end of command line */
    gCommandBuffer[idx] = '\0';
    gParamBuffer[idx2] = '\0';
    idx = 0;
    idx2 = 0;
    state = COMMAND;    
    return true;
  gCommandBuffer[idx] = TO_UPPER(nextChar);
  return false;
}  /* END  BldCmds */ 

void ProcessCmd(void)
int bCommandFound = false;
int idx;
        /* Command & options */
      while ((str = strtok_r(cmdptr, ";", &cmdptr)) != NULL) { /* semicolon delimiter */
            /* Function w/o commands */
            str1 = strtok_r(str, "-", &str);
                  /* Options to function */
                  optptr = strtok_r(str, ";", &str);

            for (idx = 0; gCommandTable[idx].name != NULL; idx++) {

                  if (strcmp(gCommandTable[idx].name, str1) == 0) {/* Found CMD */
                        bCommandFound = true;

            /* Check for options */
            if ( optptr != '\0' ) {
                  gParamValue = true;

            if (bCommandFound == true) { /* Execute command */
                  bCommandFound = false;
            }else {
                  printf("\t\t[%s] Not Found",str1);
                      bCommandFound = false;
      gParamValue = false;
      printf("%s", NL);
      } /* End while */
}/* END ProcessCmd */

void prompt() {printf("%s", PROMPT);}

/* blip throttle N times then return to start position */
void blip() { 
int c = 1, pos, cycles = 0, sleep= 700, blipthrottle = 0;
char *opt;

      printf("%sblip:", NL);
      if ( (CPOS < Lthrottle) || (CPOS > Hthrottle) ) {
            printf(" CPOS out of range%s", NL);
      blipthrottle = CPOS * 1.4;
      if(blipthrottle > Hthrottle)
            blipthrottle = Hthrottle;

      if (gParamValue == true) {
            opt = strtok_r(optptr, "-", &optptr); /* take only the first option */
            /* Convert the parameter to an integer value. */
            /* If the parameter is empty, cycles becomes 1 */      
            cycles = strtol(opt, NULL, 0);
      } else {
            cycles = 1;
      pos = CPOS; /* save */
if(cycles > 1){
printf("BLIP RUN: [%d]",blipthrottle);
      do { /* at least once */
if(cycles > 1){
printf("%d ", c);

      } while(c <= cycles);
      gParamValue = false;
      delay(DELAY); /* settle time */
      CPOS = pos; /* restore */
}/* END blip() */

void help() { }
void menu() {

  printf("\tUsage:  [Action](-Option); %s%s", NL, NL);
  printf("\tSeparate multiple action with ';' character%s%s", NL, NL);
  printf("\t[Action](-Option);Action;Action(-option);Action;%s%s", NL, NL); 

  printf("\texit              Stop program, throttle to idle, ignition shutdown %s", NL);
  printf("                      stop kill e s k also work%s%s", NL, NL);
  printf("\tcenter%s", NL);
  printf("\thight%s", NL);
  printf("\tidle%s", NL);
  printf("\tfullt%s", NL);
  printf("\tblip%s", NL);
  printf("\tcycle%s", NL);
  printf("\tmidt%s", NL);
  printf("\tmanual%s", NL);
  printf("\tmenu%s", NL); 

}/* END menu() */

void idle() {/* Move to idle */
int i;
      printf("%sidle:", NL);
      CPOS = Lthrottle;
}/* END idle() */

void stopProgram(){/* Shutdown program */
      printf("%s\t%s shutdown%s", NL, PROGRAM, NL);
      delay(100); /* settle time */

This sounds like it would make for a neat kit, if you simplified things and added an LCD plus a few buttons. Packaged up with an RC servo output connector, the buttons and LCD, plus a panic button, and whatever else - you could eliminate the serial connection entirely (or leave it there for data collection). There really wouldn't be a need for a serial-based terminal menu, and it would be simple for others to use for this task.

Certainly sounds interesting!


2 gallons for a run in?! Are you serious? I'd go for half hour bench run in, then maybe take it easy, not to much full throttle for the first couple of hours. If you wanted to use an Arduino, set it up on board with a temperature sensor and a throttle servo pass through, when the engine starts to get a bit too warm, get your Arduino to throttle down. But a 2 gallon bench run in is a "tad" excessive.

Engine (55cc) is considered broken in after 2 gallons. About 16 flights. I don't fly every week; or only 1 plane. Besides, I like to be "ready to run off the trailer".

But heh; this is an Arduino forum, I'm having trouble getting interrupts to work the way I'd like. What I want is 2 panics. The first will be a button at the test bench; press it to shut off the ignition. The 2nd, and the one I'm having trouble with, is a program interrupt. What I want to happen is: press any key (on keyboard) during run, get ignition off, closed throttle, program stop.

I read through your code again. I see what you're trying to do, I'll load the sketch and see what happens.

I would strongly suggest you put a some kind of temperature sensor on the cylinder head, be it a simple thermistor hooked up to an analog input or one of those cute Dallas 1WIRE sensors.

But like I said, I'd run it for maybe an hour or 2, then put it in your plane and just take it easy. Is this a 2 stroke or 4 stroke engine? Either way the point of run in time is to bed the rings and on a 2 stroke bed the big end and gudgeon pin bearing.

You'll get a lot more engine life running it in with a varied load, you don't get that cycling the throttle on a bench run.

This is cheating lol. But yea put a temp sensor on the head. If at a certain temp reaches either have some thing pinch the fuel line or pull the glow plug.

Screw that! Have the Arduino throttle back! Use 2 temp sensors, 1 for cylinder temp, if it's a 4 stroke the other for oil temp, which is a better indication of engine temperature anyway. Go crazy, get a free oxygen sensor, 2 servos, one for throttle, one for mixture, then add an RPM sensor! A complete EMS! (engine management system)

Now that's OVERKILL!!!! :)

Go crazy, get a free oxygen sensor, 2 servos, one for throttle, one for mixture, then add an RPM sensor!
A complete EMS! (engine management system)
Homeboy dyno cell :slight_smile:
Someone did ask already.

This is cheating lol. But yea put a temp sensor on the head. If at a certain temp reaches either have some thing pinch the fuel line or pull the glow plug.
I do have a temp sensor on head but it’s a standalone unit. I use an ignition switch that plugs into a servo slot on receiver. I use one of my channels on the transmitter for a kill switch, but this will change.

I could buy all this but I’d rather put it together myself. See this youtube vid; look at test stand equipment: http://www.youtube.com/watch?v=YToE-cbD8Q4
Go to this web site http://www.medusaproducts.com/Power-Analyzers/pa-60100R.htm and look at Power Analyzer Pro package.

What kind of motor are we talking about? The hardware store in Mt Barker had Ryobi brush cutters on special for AUS$130, so I got 1, with a free pair of electronic noise cancelling earmuffs! (not the bluetooth ones) I'm going to make a prop plate for it and considering keeping the centrifugal clutch so the prop free-wheels.


weighs 19 oz. less then the 40cc converted engine I pulled off the plane; and hand flip starts.