Beta testers wanted for TinyServo library

After making my Hexapod robot, I decided to go whole hog and make a servo library for ATTiny 84, 85, and 2313 using the Google Code Archive - Long-term storage for Google Code Project Hosting. Core.

I tested it on my ATtiny 84 and 85, but I just have a cheap analog micro servo so it would be good to get more testers. Also I don't have a 2313 so it would be nice to see if it works there too.

The library consumes roughly 750 bytes of flash and (I think) 10 bytes of RAM. This is nearly half the flash required by SoftwareServo, and unlike SoftwareServo, needs no (indeed, has no) refresh() function in loop().

On ATTiny85, you can run at 8MHz or 16MHz, but for 8MHz it's recommended (but not required) that you edit /hardware/arduino/cores/tiny/core_build_options.h and move the Core to Timer0.

Disclaimer: Servo damage may occur! The library will send 600uS to 2400uS pulses which may be beyond the physical reach of some servos, so use with care.

Attached below.

TinyServo.zip (4.45 KB)

Hi,
Thanks for your work on this. I will try to get a ATTINY going to try this. I usually am doing all 328 stuff.

OT somewhat: Since you are understanding Timers better than most, please see:
http://forum.arduino.cc/index.php?topic=199930.0

Thanks!

Thanks for sharing
I just tried on ATtiny2313

I had to change
TIMSK1
In line 37 in to
TIMSK

and in line 71
TIM1_COMPA_vect
should be
TIMER1_COMPA_vect

After these changes it works (ATtiny2313 @8MHZ, no changes to the core timer setting)

I used both cheap analog and digital servo (TowerPro MG16r)

It seems that the speed is fasterv than when used on a ATtiny85, maybe the prescaler settings ?

Thanks Erni! The zip attached to the first post of this thread has been updated to reflect your changes.

When trying to compile the example (using Arduino IDE 1.0.5) with ATtiny85 @ 8Mhz setting I get errors:

In file included from TinyServo.cpp:7:
/TinyServo.h:77:4: error: #error TinyServo library does not support this processor.
TinyServo.cpp: In function 'void moveServo(byte, byte)':
TinyServo.cpp:25: error: 'SERVOMAX' was not declared in this scope
TinyServo.cpp: In function 'void setupServos()':
TinyServo.cpp:64: error: 'OCRxA' was not declared in this scope
TinyServo.cpp: At global scope:
TinyServo.cpp:97: error: expected unqualified-id before 'if'
TinyServo.cpp:104: error: expected unqualified-id before 'else'

From the pre-processor DEFINE statements, it is obvious you were targeting to support ATtiny85 chips. I am not an expert on pre-processor statements, but am thinking I need to predefine some statement. Can someone help me resolve these errors?

I had the .h/.cpp files in same folder as the example sketch. I did not modify any of the files.

Thank you Krist

I had the .h/.cpp files in same folder as the example sketch.

They should be in the TinyServo folder

I have tried having the files in the TinyServo folder under Libraries [..\Arduino\libraries\TinyServo]. Compiling the Example in the examples folder [..\Arduino\libraries\TinyServo\examples\TinyServoExample\TinyServoExample.ino] still did not compile. Get same compiler error.

In file included from TinyServoExample.ino:39:
C:\Users\Krist\Documents\Arduino\libraries\TinyServo/TinyServo.h:77:4: error: #error TinyServo library does not support this processor.
TinyServoExample.ino: In function 'void loop()':
TinyServoExample:97: error: 'SERVOMAX' was not declared in this scope

Not sure why. I have used other user built libraries and examples without trouble. I am not seeing anything obvious in the code, and obviously others have the library working.

norlander,

What Core are you using, and what boards.txt entry are you selecting? I use these:

http://code.google.com/p/arduino-tiny/

##############################################################

attiny85at8.name=ATtiny85 @ 8 MHz (internal oscillator; BOD disabled)

# The following do NOT work...
# attiny85at8.upload.using=avrispv2
# attiny85at8.upload.using=Pololu USB AVR Programmer

# The following DO work (pick one)...
attiny85at8.upload.using=arduino:arduinoisp
# attiny85at8.upload.protocol=avrispv2
# attiny85at8.upload.using=pololu

attiny85at8.upload.maximum_size=8192

# Default clock (slowly rising power; long delay to clock; 8 MHz internal)
# Int. RC Osc. 8 MHz; Start-up time PWRDWN/RESET: 6 CK/14 CK + 64 ms; [CKSEL=0010 SUT=10]; default value
# Brown-out detection disabled; [BODLEVEL=111]
# Preserve EEPROM memory through the Chip Erase cycle; [EESAVE=0]

attiny85at8.bootloader.low_fuses=0xE2
attiny85at8.bootloader.high_fuses=0xD7
attiny85at8.bootloader.extended_fuses=0xFF
attiny85at8.bootloader.path=empty
attiny85at8.bootloader.file=empty85at8.hex

attiny85at8.build.mcu=attiny85
attiny85at8.build.f_cpu=8000000L
attiny85at8.build.core=tiny

###########################################################################

attiny85at16p.name=ATtiny85 @ 16 MHz (internal PLL; 4.3 V BOD)

# The following do NOT work...
# attiny85at16p.upload.using=avrispv2
# attiny85at16p.upload.using=Pololu USB AVR Programmer

# The following DO work (pick one)...
attiny85at16p.upload.using=arduino:arduinoisp
# attiny85at16p.upload.protocol=avrispv2
# attiny85at16p.upload.using=pololu

attiny85at16p.upload.maximum_size=8192

# PLL Clock; Start-up time PWRDWN/RESET: 1K CK/14 CK + 4 ms; [CKSEL=0001 SUT=00]
# Brown-out detection level at VCC=4.3 V; [BODLEVEL=100]
# Preserve EEPROM memory through the Chip Erase cycle; [EESAVE=0]
# Serial program downloading (SPI) enabled; [SPIEN=0]

attiny85at16p.bootloader.low_fuses=0xC1
attiny85at16p.bootloader.high_fuses=0xD4
attiny85at16p.bootloader.extended_fuses=0xFF
attiny85at16p.bootloader.path=empty
attiny85at16p.bootloader.file=empty85at16.hex

attiny85at16p.build.mcu=attiny85
attiny85at16p.build.f_cpu=16000000L
attiny85at16p.build.core=tiny

thank you...let me try this. I was using cores I got from http://hlt.media.mit.edu/?p=1229. Had boards.txt that included the below...I used "attiny85-8.name=ATtiny85 (internal 8 MHz clock)". I will try using this other hardware core you suggest. I am assuming I just load those files into the "Hardware" folder like I did for the other cores.

attiny85.name=ATtiny85 (internal 1 MHz clock)
attiny85.bootloader.low_fuses=0x62
attiny85.bootloader.high_fuses=0xdf
attiny85.bootloader.extended_fuses=0xff
attiny85.upload.maximum_size=8192
attiny85.build.mcu=attiny85
attiny85.build.f_cpu=1000000L
attiny85.build.core=arduino:arduino
attiny85.build.variant=tiny8

attiny85-8.name=ATtiny85 (internal 8 MHz clock)
attiny85-8.bootloader.low_fuses=0xe2
attiny85-8.bootloader.high_fuses=0xdf
attiny85-8.bootloader.extended_fuses=0xff
attiny85-8.upload.maximum_size=8192
attiny85-8.build.mcu=attiny85
attiny85-8.build.f_cpu=8000000L
attiny85-8.build.core=arduino:arduino
attiny85-8.build.variant=tiny8

attiny85-20.name=ATtiny85 (external 20 MHz clock)
attiny85-20.bootloader.low_fuses=0xfe
attiny85-20.bootloader.high_fuses=0xdf
attiny85-20.bootloader.extended_fuses=0xff
attiny85-20.upload.maximum_size=8192
attiny85-20.build.mcu=attiny85
attiny85-20.build.f_cpu=20000000L
attiny85-20.build.core=arduino:arduino
attiny85-20.build.variant=tiny8

perfect feedback...thank you. I used new core files for Arduino 1.0, put them in Hardware folder, and changed the "Prospective Boards.txt" file to just "boards.txt". Servo is running as expected with example code! Thank you very much!

The servo performance is better [smoother] than the library I was using before.

I will play with this library more and provide whatever feedback I can.

again, thank you.
Krist

question before I get into my beta testing. For power conservation reasons, I am going to putting my ATtiny85 into extended sleep modes. I will be putting into the cyclic sleep using the watchdog timer [setup_watchdog(WDTO_8S)] and repeatedly wake/sleep for a specified number of cycles and then enabling servo commands (along with some other activity). Are you already aware of issues that may arise from using your library under these conditions because of how you are using the timers?

If I run into any anomalies, I will share them in this forum.

Well glad you got it working. I'm not familiar with the HLT core but I assume the constants for CPU definition are different or don't exist.

As for sleeping, I haven't tested that but I since interrupts are disabled when sleeping, one of the servo pins would get left in a HIGH state when sleeping which may send the servo to the extreme end of travel. To sleep safely, I would recommend something like the following sequence. Whether you need to disable/enable Timer0 or Timer1 depends on if you have moved millis(). The below example assumes TinyServo is using Timer1, substitute OCIE0A for OCIE1A if TinyServo is using Timer0.

TIMSK &= ~(1 << OCIE1A); // Disable Timer/Counter1 Output Compare Match A Interrupt, preserving other bits 
digitalWrite(servoPin[currentServo], LOW);
// sleep here
TIMSK |= (1 << OCIE1A); // Timer/Counter1 Output Compare Match A Interrupt Enable, preserving other bits

I'm not certain this will work but it's a good place to start. I suppose I should upgrade the library to add a "detach servo" function for situations like this. So, thanks for the feedback, you found a use case I missed. :slight_smile:

I tried it and with ATtiny2313 @ 8 MHz, with the following boards.txt code

attiny2313at8.name=ATtiny2313 @ 8 MHz

# The following do NOT work...
# attiny2313at8.upload.using=avrispv2
# attiny2313at8.upload.using=Pololu USB AVR Programmer

# The following DO work (pick one)...
attiny2313at8.upload.using=arduino:arduinoisp
# attiny2313at8.upload.protocol=avrispv2
# attiny2313at8.upload.using=pololu

attiny2313at8.upload.maximum_size=2048

# Default clock (slowly rising power; long delay to clock; 8 MHz internal)
# Int. RC Osc. 8 MHz; Start-up time: 14 CK + 65 ms; [CKSEL=0100 SUT=10]; default value 
# Brown-out detection disabled; [BODLEVEL=111]
# Serial program downloading (SPI) enabled; [SPIEN=0]
# Preserve EEPROM memory through the Chip Erase cycle; [EESAVE=0]

attiny2313at8.bootloader.low_fuses=0xE4
attiny2313at8.bootloader.high_fuses=0x9F
attiny2313at8.bootloader.extended_fuses=0xFF
attiny2313at8.bootloader.path=empty
attiny2313at8.bootloader.file=empty2313at8.hex

attiny2313at8.build.mcu=attiny2313
attiny2313at8.build.f_cpu=8000000L
attiny2313at8.build.core=tiny

the example code compiles ok (haven't tested it on the core itself)
but if I select ATtiny2313 @ 1 MHz (with this code):

attiny2313at1.name=ATtiny2313 @ 1 MHz

# The following do NOT work...
# attiny2313at1.upload.using=avrispv2
# attiny2313at1.upload.using=Pololu USB AVR Programmer

# The following DO work (pick one)...
attiny2313at1.upload.using=arduino:arduinoisp
# attiny2313at1.upload.protocol=avrispv2
# attiny2313at1.upload.using=pololu

attiny2313at1.upload.maximum_size=2048

# Default clock (slowly rising power; long delay to clock; 8 MHz internal; divide clock by 8)
# Int. RC Osc. 8 MHz; Start-up time: 14 CK + 65 ms; [CKSEL=0100 SUT=10]; default value 
# Divide clock by 8 internally; [CKDIV8=0]
# Brown-out detection disabled; [BODLEVEL=111]
# Serial program downloading (SPI) enabled; [SPIEN=0]
# Preserve EEPROM memory through the Chip Erase cycle; [EESAVE=0]

attiny2313at1.bootloader.low_fuses=0x64
attiny2313at1.bootloader.high_fuses=0x9F
attiny2313at1.bootloader.extended_fuses=0xFF
attiny2313at1.bootloader.path=empty
attiny2313at1.bootloader.file=empty2313at1.hex

attiny2313at1.build.mcu=attiny2313
attiny2313at1.build.f_cpu=1000000L
attiny2313at1.build.core=tiny

then it fails to compile with:

In file included from TinyServoExample.ino:39:0:
~/sketchbook/libraries/TinyServo/TinyServo.h:26:6: error: #error TinyServo does not support this CPU clock speed
     #error TinyServo does not support this CPU clock speed
      ^
TinyServoExample.ino: In function ‘void loop()’:
TinyServoExample.ino:97:23: error: ‘SERVOMAX’ was not declared in this scope

Hi quixote_arg,

Thanks for trying it out. I'm afraid that the library does not support 1MHz. If I ever get around to updating the library to include a 'detach' function, I will see about supporting 1MHz clock speeds as well.

In the meantime, you should be able to tweak it to work at 1MHz though. In the ATtinyX313 section of setupServos() in the .cpp, change the Timer1 prescaler from 64 to 8 by removing the CS10 bit:

 TCCR1B |= (1 << CS11); // prescale of 8, preserving existing bits

Then edit the .h file and in the ATtinyX313 section change the #if from 8000000 to 1000000. I think that will do the trick.

thanks for the quick reply

I was under the false assumption that in order to run a tiny2313@8MHz I needed an external crystal, which isn't the case, so I can run the code fine @8MHz

UPDATE: The Zip attached to the first post of this thread has been updated with a bugfix for ATtiny84.

Thanks !
Very helpfull and instrumental in completing my little project
This worked first time try on a ATTiny85 @ 8MHz. I could not get a stable servo signal @16MHz

Hans

Thanks hmeijdam! :slight_smile:

As for 16Mhz problems, I can only guess that perhaps the "Burn Bootloader" process didn't work to set the fuses.

tylernt:
Thanks hmeijdam! :slight_smile:

As for 16Mhz problems, I can only guess that perhaps the "Burn Bootloader" process didn't work to set the fuses.

I double checked again today and setting the fuses (burning bootloader) for 16MHz seems working fine.
I tested this using the blink sketch to check if at both 8MHz and 16MHz it still blinks at a one second rate.
I use exactly the same core and boards.txt files that you described above. I have your library running at 8MHz now and cannot afford to change the core timer, as that is in use for the "manchester" coding datatransmission protocol. This is why I am so keen on getting it to 16Mhz. Better servo resolution with less jitter.

I know that blink is using the core timer, so maybe the timer 1 that I have to use at 16MHz is not properly configured. Did you test 16MHz with both timers?
Below is the code that I use for testing (I also tested your example) and both work fine on 8MHz but on 16MHz the servo moves to maximum end-point and starts humming. I wish I had a scope to see what pulse comes out.

/*
Servo connected to Pin 1 and Potmeter to Pin A3 

                  ATtiny25/45/85
                      +-\/-+
              RESET  1|    |8  VCC
               3/A3  2|    |7  2/A1
               4/A2  3|    |6  1
                GND  4|    |5  0
                      +----+

*/
int analog1 = A3; //Analog Input

#include <TinyServo.h>
const byte SERVOS = 1; // how many servos do you have? up to 5 on ATTiny85 and 8 on ATtiny84/2313
const byte servoPin[SERVOS] = {1}; // what pins are your servos on?
// you have the option to give your servos nice names. 0 refers to the first servo pin above, 1 to the second, etc
#define SERVO1 0

void setup(){
pinMode(analog1,INPUT);
setupServos();
}

void loop(){
int an = analogRead(analog1);
an = map(an, 0, 1023, 0 , 180 );
moveServo(SERVO1, an);
delay(20); // with or without this delay the result was the same
}

I just tried the library on ATTiny85 @ 16 MHz.
It worked fine, see the attached screenshot from my logic analyzer.
The pulsewidth is 1 ms

tyler_servo.jpg