Skip to content

9. Output devices

This is a link to this week’s group assignment, in which we measured the power consumption of an output device (the servo).

Output device: servo

(TODO: add link to previous assignment page)

To try using servos, I decided to use the SG90 servos which were found in the lab. I connected the servo to my ATTiny412 board I designed and produced last week through the header and extra wires. This meant I connected the voltage on the servo (red) to VCC on the board, the ground on the servo (brown) to ground on the board, and the PWM/signal wire on the servo (orange) to another open pin on the board.

To get the servo to work, I found an example within the Arduino IDE, at File -> Examples -> megaTinyCore -> Sweep. This contained an example to interface with the Servo library which was adapted by the megaTinyCore authors to work on the ATTiny MCUs. The code content of this example:

#include <Servo_megaTinyCore.h>

Servo myservo;  // create servo object to control a servo
// twelve servo objects can be created on most boards

int pos = 0;    // variable to store the servo position

void setup() {
  myservo.attach(9);  // attaches the servo on pin 9 to the servo object
}

void loop() {
  for (pos = 0; pos <= 180; pos += 1) { // goes from 0 degrees to 180 degrees
    // in steps of 1 degree
    myservo.write(pos);              // tell servo to go to position in variable 'pos'
    delay(15);                       // waits 15ms for the servo to reach the position
  }
  for (pos = 180; pos >= 0; pos -= 1) { // goes from 180 degrees to 0 degrees
    myservo.write(pos);              // tell servo to go to position in variable 'pos'
    delay(15);                       // waits 15ms for the servo to reach the position
  }
}

I adapted this example code to be more concise and also have some parameters as #defines in the code:

#include <Servo_megaTinyCore.h>
Servo servo;
#define delay_between_write 5
#define pause_time 0
#define write_change 1
#define servo_pin PIN_PA1
void setup() {
  servo.attach(servo_pin);
}
void loop() {
  for (int deg = 180; deg >= 0; servo.write( write_change+(deg-=write_change))) delay(delay_between_write);
  delay(pause_time);
  for (int deg = 0; deg <= 180; servo.write(-write_change+(deg+=write_change))) delay(delay_between_write);
  delay(pause_time);
}

After deploying this code and running it, the code successfully moved the servo:

Note on pulse widths

I found a collection of random datasheets for this servo with conflicting specs, apparently combined into a single pdf document as https://www.friendlywire.com/projects/ne555-servo-safe/SG90-datasheet.pdf. I’m guessing that what happened with all the datasheets was that individual stores selling this servo each made their own datasheet. Some of these datasheets listed the necessary duty cycle range as “1 ms - 2 ms” but others listed it as 0.5 ms - 2.4 ms (500 µs - 2400 µs).

When I was looking at the code of Servo_megaTinyCore to understand how it worked, it listed MIN_PULSE_WIDTH = 544 and MAX_PULSE_WIDTH = 2400. It appears that this was because many servos accept pulse widths beyond the range in the datasheet (from this Stack Exchange question asking about the topic).

I tried adapting my program to write pulse widths between 1000µs - 2000µs and the servo did not move in the full 180º range, so 0.5ms - 2.4ms seems like it would be an accurate enough pulse width range for this servo, which is good because it’s already the default in the servo library.