ServoTester


Here is a really small side project that I developed while working on something else…

I had to test a RC speed controller that I wanted to use to control an electric car window motor, and for the 100th time I was facing the same dilemma: find 8 batteries for my remote control, dismount the RX part from the quadcopter and use that, OR grab the Arduino and write quickly some code to generate the corresponding signals ? Neither of which was actually particularly handy…

So I finally decided to build a small stand alone servo tester :

The hard part was to avoid trying to squeeze too many features in it, as I kept thinking that having an LCD screen showing the exact pulse width in microsecs would be useful, etc. ….

I finally managed to convince myself that “less is more” and the final design simply has a micro-controller (what else than the trustworthy AtTiny85 !?  :) )  and a big knob (that I salvaged from a radio).

Here’s some sort of schema, though that’s probably an overstatement :)

Servo Tester Schema

Servo Tester Schema

Servo Tester

Servo Tester

A, yes, sorry there is also a small capacitor to avoid noise from the motor affecting the MCU (see the lesson I’ve learned here, the HARD way !) and a LED, to give some basic visual feedback, at least when the device is operating.

And here is the simple and clean soldering at the back (I’m becoming quite good at this, it’s almost like a craft: initially how to position the parts to minimize the distances and then how to re-use existing connections to minimize extra soldering… :) ), I probably should one day make the leap to design proper PCBs…

Servo Tester Back

Servo Tester Back

And as usual, here’s the code that runs on the AtTiny85.

I contemplated the idea of using AVR C code directly, but it’s so much easier to use Arduino and the existing libraries…

One remark before you look at the code: you might find it unnecessarily complex… The reason is that either because the potentiometer is of low quality or maybe that’s normal with these mechanical devices, the readings can be VERY noisy, especially while it’s moving ! So half of the code simply tries to filter that out…

/*
* trandi 15 June 2013
* This could have been MUCH simpler, if it wasn't for a noisy and bad potentiomenter,
* and hence the need to filter the input data.
*/

#include <SoftwareServo.h>

#define PIN_SERVO 0
#define PIN_POT 2
#define PULSE_MIN 600 // in microsecs
#define PULSE_MAX 2400 // in microsecs
#define READINGS_COUNT 10

SoftwareServo servo;
long potValue = 0;
int readings[READINGS_COUNT] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
byte readingsPos = 0;
unsigned long nextReadingDue = 0;

void setup(){
servo.attach(PIN_SERVO);
servo.setMinimumPulse(PULSE_MIN);
servo.setMaximumPulse(PULSE_MAX);
}

void loop(){
// read the analog pin once every millisec, and store the data until we have enough to filter
unsigned long currentTime = micros();
if(currentTime > nextReadingDue){
readings[readingsPos] = analogRead(PIN_POT);
readingsPos ++;

nextReadingDue = currentTime + 1000;
}

// once every READINGS_COUNT readings, clean the data and update the servo
// also filter using weighted moving average to avoid excessive jittering due to poor potentiometer
if(readingsPos > READINGS_COUNT){
potValue = (9 * potValue + getCleanReading())/ 10;

servo.write(map(potValue, 0, 1023, 0, 180));

readingsPos = 0;
}

// good to call this as often as we can, so that we don't miss the moment when the signal has to go up or down
SoftwareServo::refresh();
}

// reads several analog values and tries to do some smart filtering
int getCleanReading(){
int reading, minReading=9999, maxReading=0;
long result = 0;

for(byte i=0; i<READINGS_COUNT; i++){
readings[i] = analogRead(PIN_POT);
result += readings[i];

if(readings[i] < minReading){
minReading = readings[i];
}
if(readings[i] > maxReading){
maxReading = readings[i];
}
}

// reaturn the average after eliminating min and max readings
return (result - minReading - maxReading) / (READINGS_COUNT - 2);
}
About these ads

26 Responses to ServoTester

  1. Iván says:

    Hi!
    How do you upload your code into attiny? it always give me several compiling errors like:
    (x86)\Arduino\libraries\SoftwareServo\SoftwareServo.cpp:27: error: ‘digitalWrite’ was not declared in this scope
    C:\Program Files

    I use:
    board->attiny85(8Mhz)
    programer->ArduinoasISP
    arduino version->1.0.5 r2
    atmega328

    thank you

  2. rb salyer says:

    I know I’m getting here late, but I really want one of these, but have no way of programming the chip…is there a chance you would build one and sell it to me, or program the chip and sell me a programmed chip?

    This is exactly what I need…

  3. Dustan says:

    I cant seem to get this working. I am using Duemilanove board to program the Attiny85 and am trying to control a esc for a brushless motor which need a pulse of 1000 to 2000 for no speed to full speed. I can load the code but cant seem to get it to work. I have a attiny8520pu chip what option do I choose as my board? I chose 8 MHz internal Osc. and chose arduino ISP as programmer after I loaded the ISP example to the board but I cant cant get the esc to start up (I have already used the duemilinove board to control the esc so i know it works using servo library but cant get it to work with this do i need some other component? thanks

    • trandi says:

      Have you connected the LED? Is it blinking, so that you can at least check that the ATtiny85 is programmed correctly ?
      Also, by default the Attiny comes with the fuses set to use the 1MHz internal oscillator, so unless you’ve updated that, then try with that option and it should work.

      Let me know if this helps?
      Dan

      • Dustan says:

        The led in your schematic looked like it just showed power on-off. but i will. where did you get the softwareservo library? i found 2 and one wont compile one will but doesnt work. also how do i change thefuses setting? thanks again

      • trandi says:

        - yes you’re right, my bad, I thought it was blinking depending on the pulse freq… :)

        – can’t remember where I got the library. For the one that doesn’t compile, you” probably need to replace #include with #include “Arduino.h” in SoftwareServo.h, as the former is NOT compatible with Arduino IDE > 1.0

        – you can change the fuses with AVR Studio, but it’s much easier to just use the 1MHz option from the Arduino drop down and leave your MCU at the default frequency

        Dan

      • dustan says:

        I put an led on the servo output pin and it blinks but doesnt change with the pot. I have tried a few different codes but still cant get it to work been tryin for two days now

      • trandi says:

        How about the other advice, using the 1MHz option instead of the 8MHz one ??

      • Dustan says:

        that doesnt seem to make a difference. Sorry bout the long time between reply been working in my shop and running back into the house to apply changes but takes some time at the shop

    • Dustan says:

      ok I finally made some progress It works but is really choppy and unstable pulse. not sure if this is ok to post the code here but this is what i have to get the 1000 to 2000 pulse I think the main problem was the softwareservo library I had.
      /*
      * trandi 15 June 2013
      * This could have been MUCH simpler, if it wasn’t for a noisy and bad potentiomenter,
      * and hence the need to filter the input data.
      */

      #include

      #define PIN_SERVO 0
      #define PIN_POT 3
      #define PULSE_MIN 1000 // in microsecs
      #define PULSE_MAX 2000 // in microsecs
      #define READINGS_COUNT 10

      SoftwareServo servo;
      long potValue = 0;
      int readings[READINGS_COUNT] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
      byte readingsPos = 0;
      unsigned long nextReadingDue = 0;

      void setup(){
      servo.attach(PIN_SERVO);
      servo.setMinimumPulse(PULSE_MIN);
      servo.setMaximumPulse(PULSE_MAX);
      }

      void loop(){
      // read the analog pin once every millisec, and store the data until we have enough to filter
      unsigned long currentTime = micros();
      if(currentTime > nextReadingDue){
      readings[readingsPos] = analogRead(PIN_POT);
      readingsPos ++;

      nextReadingDue = currentTime + 1000;
      }

      // once every READINGS_COUNT readings, clean the data and update the servo
      // also filter using weighted moving average to avoid excessive jittering due to poor potentiometer
      if(readingsPos > READINGS_COUNT){
      potValue = (9 * potValue + getCleanReading())/ 10;

      servo.write(map(potValue, 0, 1023, 0, 180));

      readingsPos = 0;
      }

      // good to call this as often as we can, so that we don’t miss the moment when the signal has to go up or down
      SoftwareServo::refresh();
      }

      // reads several analog values and tries to do some smart filtering
      int getCleanReading(){
      int reading, minReading=9999, maxReading=0;
      long result = 0;

      for(byte i=0; i<READINGS_COUNT; i++){
      readings[i] = analogRead(PIN_POT);
      result += readings[i];

      if(readings[i] maxReading){
      maxReading = readings[i];
      }
      }

      // reaturn the average after eliminating min and max readings
      return (result – minReading – maxReading) / (READINGS_COUNT – 2);
      }

      • trandi says:

        It would be easier if you posted JUST the differences, so that I don’t have to go through all the code and compare…:) From what I see, you’ve only removed: if(readings[i] < minReading){ minReading = readings[i]; }

        Which simply means that minReading will ALWAYS be 9999. So when averaging this across 8 readings, it simply means you're offseting the signal by 1250 microsecs.

        Now that I think about it, it might be that 1MHz is too slow for the software servo library, which would explain the erratic behaviour and jittering.

        Google how to change the fuses on an Attiny (I personally use AVRStudio) and make sure you make it use the 8MHz internal oscillator (basically simply removing the division by 8) and then re-program the MCU and we shall see…

        Dan

      • Dustan says:

        Ok sorry about that. I chose the 8MHz board and burnt the bootloader and i think it changed the fuses. It works much smoother now. Its a little sluggish to react to quick changes is this because of the filtering you do? Other than that it seems to be working and will work for what i need Thanks for your help

      • trandi says:

        OK, great, even simpler than my solution with the AVRStudio ! So that confirms that it was indeed the problem with 1MHz being too low… now that we discuss about it, I think I remember I had the same problem, I wanted to use the default config initially but had to update to 8MHz.

        Exactly, the sluggishness is due to filtering, as I found that my potentiometer was giving VERY noisy readings, and the servo would jump all over the place. If you have a better pot, then you can make the code MUCH simpler, as 75% of it is the filtering… Or you can reduce the READINGS_COUNT to less than 10, so that it averages over less data…

        Anyhow, I’m glad I could help you and that you found my project useful !

        Out of curiosity, what do need it for, I’d love to hear more about your project ?

        Dan

      • Dustan says:

        I am using an esc with a brushless motor and ducted fan as sort of mini turbo booster for a small gasoline engine Ive been messing with HHO and gasoline vapor carburetor. just wanted to set the potentiometer for certain fan speed (boost) to do my tests with and didnt want to use my only arduino. plus I also would like to use it for testing servos and other esc’s this is a handy little option Thanks again

  4. I get the following error:

    /Users/santiagolopezpina/Documents/Arduino/libraries/SoftwareServo/SoftwareServo.cpp: In member function ‘uint8_t SoftwareServo::attach(int)':
    /Users/santiagolopezpina/Documents/Arduino/libraries/SoftwareServo/SoftwareServo.cpp:27: error: ‘digitalWrite’ was not declared in this scope
    /Users/santiagolopezpina/Documents/Arduino/libraries/SoftwareServo/SoftwareServo.cpp:28: error: ‘OUTPUT’ was not declared in this scope
    /Users/santiagolopezpina/Documents/Arduino/libraries/SoftwareServo/SoftwareServo.cpp:28: error: ‘pinMode’ was not declared in this scope
    /Users/santiagolopezpina/Documents/Arduino/libraries/SoftwareServo/SoftwareServo.cpp: In member function ‘void SoftwareServo::write(int)':
    /Users/santiagolopezpina/Documents/Arduino/libraries/SoftwareServo/SoftwareServo.cpp:51: error: ‘clockCyclesPerMicrosecond’ was not declared in this scope
    /Users/santiagolopezpina/Documents/Arduino/libraries/SoftwareServo/SoftwareServo.cpp: In static member function ‘static void SoftwareServo::refresh()':
    /Users/santiagolopezpina/Documents/Arduino/libraries/SoftwareServo/SoftwareServo.cpp:73: error: ‘millis’ was not declared in this scope
    /Users/santiagolopezpina/Documents/Arduino/libraries/SoftwareServo/SoftwareServo.cpp:106: error: ‘digitalWrite’ was not declared in this scope
    /Users/santiagolopezpina/Documents/Arduino/libraries/SoftwareServo/SoftwareServo.cpp:108: error: ‘TCNT0′ was not declared in this scope
    /Users/santiagolopezpina/Documents/Arduino/libraries/SoftwareServo/SoftwareServo.cpp:123: error: ‘digitalWrite’ was not declared in this scope

    I’m using the MIT files to attiiny and arduino 1.0.5.

    Do you know wich is the mistake?

  5. Got the code perfectly, but there is one small problem. Would you tell me the rating of the capacitor? I didn’t use it, and it worked fine. I want to see if it betters the performance plus I don’t wanna harm my board with any capacitor. Anyway, it was as good as always! Works!

  6. muchyi says:

    Hello, good idea, but I have problem:

    sketch_jun26a.ino: In function ‘int getCleanReading()’:
    sketch_jun26a.ino:57:1: error: expected ‘;’ before ‘result’
    sketch_jun26a.ino:57:22: error: expected ‘)’ before ‘;’ token
    sketch_jun26a.ino:59:13: error: name lookup of ‘i’ changed for ISO ‘for’ scoping [-fpermissive]
    sketch_jun26a.ino:59:13: note: (if you use ‘-fpermissive’ G++ will accept your code)
    sketch_jun26a.ino: At global scope:
    sketch_jun26a.ino:68:1: error: expected unqualified-id before ‘return’
    sketch_jun26a.ino:69:1: error: expected declaration before ‘}’ token

    • trandi says:

      Yep a silly copy/paste error, there were a couple of lines missing.
      I’ve just fixed this, but from memory so I’m not sure it’s 100% correct, please try again and let me know.

  7. Chispter says:

    How did you get your Arduino code loaded on to the ATtiny85 chip? (There’s no programming header on your protoboard)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 127 other followers

%d bloggers like this: