11.

Output Devices

Home

Assignments

group assignment
measure the power consumption of an output device

individual assignment
add an output device to a microcontroller board you've designed
and program it to do something

Download the files

This week the only thing to download is the source code of the examples below.

Download source code files

Table of contents

Prologue

This week I decided I’d do something stupid… for a change. I didn’t want to just make the servo move because that was too simple. You can easily just bring up the Servo example with Arduino, give it the right pin, and boom, it moves.

I will cover how to use the servo with an ESP32 (in theory), then will actually do it from an Arduino (very similar technique anyway) and then I’ll use an input from an ultrasonic sensor to detect when something gets closer and actuate the servo.

If something does get close, our imaginary French citizen will wave a surrender flag. The closer the enemy gets, the faster the Frenchman waves the flag.

Understanding servo motors

Setting up our good friend the servo

My little SG90 servo

I have the standard SG90 servo motor that comes with many starter packs. I found its very simple datasheet here.

    The useful information for us today is:
  • its operating voltage, between 4.8 and 6V
  • the wiring, especially for the PWM data line
    It also informs the time of PWM pulses for their position:
  • Center needs a pulse of 1.5ms
  • -90° needs a pulse of ~1ms
  • +90° needs a pulse of ~2ms

… and how long it waits in between pulses. In this case the PWM period is 50Hz (50 times per second or 20ms).

This would be useful if we were to write our own code without using any library. As we are using the Servo.h library in the Arduino platform, we won’t have to trouble ourselves with this today.

Note //
You can do it by switch a pin to HIGH, add a delay of x milliseconds and then switch it back to low.

Wiring it up

I wanted to use the Barduino that we built before leaving the lab, but the ESP32 operates at a voltage of 3.3V and therefore cannot provide enough voltage to the servo motor.

I thought about wiring the servo motor straight to the FTDI cable that powers the board, as at this point it is the 5V 1A from the computer.

The problem is that it isn't easy to make a safe connection, the cable could easily slip and create a shortcut and fry the board.

Instead I am using an Arduino Leonardo that a friend gave me. It won’t change anything as the coding would be similar. The only thing that would change is that you would use a different library for the ESP32 than for an Arduino, but otherwise the code is quite similar (see both codes further below).

Note //
The library above allows you to write your own pulses in microseconds with writeMicroseconds()

Move it with a potentiometer

First I wanted to test the servo with a potentiometer, just to see if it all works fine.

I wired the potentiometer to the board the same way I did during the Input Devices week. Pretty simple stuff, the pins on the edges are connected to VCC and GND, doesn’t matter which way. The middle pin of the potentiometer is what you connect to the analog pin of the board (in my case the A0 of the Arduino).

alt_text

I then map the value the Arduino reads (which goes from 0 to 1023) to the degree angle (from 0 to 180°).

Note //
The servo was jittery when I was moving the potentiometer, so I used the smoothing code that Oscar gave us in Input Week.

Download Servo_w_potentiometer.ino

Measuring the power it consumes

The power consumption of a component is equal to the multiplication of the voltage by the current, so let’s write it as P = V x I

Measuring the voltage

Voltage is the measurement of potential electric energy between two points.

Usually GND is 0V, therefore if you measure from the VCC source to the GND you’ll get the full “spectrum”. In this case the Arduino provides 5V.

Note //
You could measure between VCC and the R2 resistor of a voltage divider and get a result of 1.7V, if you’re stepping down from 5V to 3.3V.
alt_text

I decided to measure the voltage as it is never exactly 5V… here it is 5.08V.

Measuring the current

An electric current is the rate of flow of electric charge past a point. In other words it’s the amount of electrons carrying charge going through a component or wire. That is why to measure current you need to make the multimeter part of the circuit - rather than being in parallel as it is when you measure voltage - so that the current goes through the multimeter.

In the first test I did with the SG90 servo, I measured that it consumed 3.1mA at rest. I tested when it rotated the fastest it could with no load attached (except that toothpick you can see above), and the maximum value it reached was 13mA. The datasheet says that the servo has a torque of 2.5kg/cm, which is quite impressive. Let’s test its power consumption with more load applied.

For the test I changed servo and used a different one. It isn’t written on it what is the model, but it doesn’t matter much for the test. I initially set my multimeter to the 20mA setting as I had a reading of around 6.7mA when the motor is at rest. It didn’t seem to want to read as it kept showing a 1, so I bumped the setting up to 200mA.

Note //
When measuring current, and any other units for that matter, the number of the settings is that maximum value you can read at that setting. If I set it up to 200mA, it means it can read up to 200mA. The value shown on the screen is in mA.

This time it does indeed show 6.7mA, I’m not sure why it didn’t want to show it in the previous setting.

Using the formula above, the servo at rest consumes 5.08 V x 0.0067A = 0.034 W… you’d need to leave it on for more than a year and half - 14 690 hours to be precise - for it consumes as much power as a 500W washing machine does in an hour.

Let’s put that little servo to work

Now let’s apply some force to it... They are surprisingly strong! I had one hand to hold it and used my finger to try and rotate the “propeller” and it didn’t budge! The highest reading I got before the propeller flew away was around 580mA.

Using the formula once again, 5.08V x 0.580A = 2.95W… now that’s better! Well, not really, but that’s more in the range we are used to. Using the same comparison it’d “only” take a week - 169.5 hours - to consume as much power as the same washing machine.

Using an input to change the output

Working the HC-SR04 ultrasonic sensor

The HC-SR04 is an ultrasonic sensor. It allows the user to measure distance by sending a sound and timing how long that sound takes to bounce back to the sensor.

The datasheet

The good ol’ datasheet, once again pretty simple but it has everything you need. I want to try and figure out how to use it myself without following tutorials.

Things to know:

  • It operates at 5V
  • It sends and receives pulses (= use PWM pins)

Things they tell but we can figure out ourselves (and will):

  • The formulate to calculate the distance (time × speed of sound / 2)
  • The minimum delay advised to use between measurements

Wiring it up

It isn’t complicated, there’s a VCC and GND pin as always, and then a pin for the Trigger and another for the Echo.

Sending mixed signals

I wasn’t sure exactly how to start. The datasheet says that you need to send a 10µs pulse to the trigger pin for the ultrasonic sensor to launch an 8 cycle sonic burst. It thens automatically generates a pulse and sends it through the echo pin. The length of that pulse is the same as the time it took in microseconds for the sound to leave and come back to the sensor.

Note //
A µs is a microsecond. It is 1000 times smaller than a microsecond, therefore a millionth of a second. More commonly written us as the µ character isn’t easily accessible on a keyboard (if at all).

I guess let’s try and start sending a 10µs pulse to the sensor then.

Generating and sending pulses on PWM pins

Using analogWrite()?

First I read about PWM and analogWrite(). The function analogWrite() takes in a value between 0 (always off) and 255 (always on) which is how long you leave a pin the HIGH state during a cycle period. The Arduino Leonardo works at 490Hz, which means an always-on pulse is 1/490Hz = ~2ms.

Note //
We can make the calculation simpler by using 500Hz so that a pulse is exactly 2ms, but as we’ll see in a second, it’s not useful in our case.

It means analogWrite(255) = 2ms, analogWrite(127) = 1ms, so the smallest pulse you can send would be 2ms / 255 = 0.0078 ms, or 7 microseconds… that’s not great, I need to send a 10 microsecond pulse. Let’s find another way.

Using delayMicroseconds() ?

After more digging around I found this article that seems to say you can simply choose a pin, make it HIGH, then use the delayMicroseconds() function and then make it LOW… so let’s try.

digitalWrite(triggerPin, HIGH);
delayMicroseconds(10);
digitalWrite(triggerPin, LOW);

echoDuration = pulseIn(echoPin, HIGH);
Serial.println(echoDuration);

It doesn’t work, it only prints 0. I wasn’t expecting it to work on the first try. The pulse might be sent, but I’m not really sure that’s the way to read the data back…

Using raw C code

I haven’t tried this technique but would like to at some point. It generates the pulses without the Arduino library. Doing it from scratch. There is also one of Neil’s many code examples which I could inspire myself from.

Reading the pulse coming in

Ah! PulseIn() seems promising. It reads a pin and measures how long it stays in HIGH in microseconds. Perfect, let’s change the echoDuration line to

echoDuration = pulseIn(echoPin, HIGH);

It didn’t work… I checked the cabling, changed the trigger pin from 5 to 3 (for no reason) and it worked! Didn’t change the code! I’m happy! If I get closer the numbers are smaller.

Wow, that wasn’t as hard as I thought! All only by reading the Arduino documentation.

Note //
I think I know why it worked when I changed to pin 3. The analogWrite() page says that pin 3 and 11 work at 980Hz. It’s possible that the other pins - that only work at 490Hz - weren’t fast enough to generate a 10 microsecond pulse?

Calculating the distance

Now we have the time in microseconds that it takes for the sound to leave and come back to the device we can calculate the distance.

Note //
In my case I don’t necessarily need to as I’m planning to only use HOW close the person is, rather than an exact distance… but let’s do it anyway, it’s pretty easy.

The formula to calculate the distance is time × speed of sound / 2. The velocity of sound is 340m/s. You divide it by 2 because this is the time it takes for the wave to do the distance we want to measure twice (there and back). Let’s keep in mind we need to use time in seconds and not microseconds.

As I’m looking at my serial monitor the readings are around 10000, so let’s use that: 0.01 x 340 / 2 = 1.7m. That sounds about right.

Note//
In the future you just need to multiply the result by 0.00017.

Time in between measurements

It says it can measure up to 4m. That means it would take...
4 = x * 340 / 2
X = 0.023529412 = 20ms
… a maximum of 20ms for the device to know the distance… it then takes an other 20ms for the device to create a pulse that long, hence why the documentation advises to use 60ms in between measurements. It allows for an extra 20ms for generating the pulse and doing something with the result.

Coding the waving of the flag

This should be quite simple. How fast the wave flags is just a function of how close the object.

I want the flag to start waving if we are 2m in front of it. If we do some reverse calculation, that means when the echoDuration is equal to 11765µs (11.76ms). It’ll wave slowly, taking about 6 seconds to go a full wave. The difference between the minAngle and the maxAngle of the wave is 80°. As we’re incrementing the steps of the servo by 1°, we want to divide 3 seconds by 80, which gives us a delay of 37.5ms between each increment.

Then the closer we get, the faster it should wave. The ultrasonic claims it can measure up to 2cm, so when echoDuration is equal to 117µs (0.117ms).

The datasheet of the SG90 says it can move at a speed of 0.12 sec/60°. Quick calculation reveals that’s 2ms per degree.

I was dubious about it and tested it with a delay of 2ms and it actually worked! I then tested with 1ms as well, and I’m not sure if it does the actual full wave but it’s good enough for me. It’s more fun, it seems like it’s actually panicking.

  • We now know that we want the delay for the wave to be every 1ms when the enemy is closest, at 117 µs.
  • We want it to be at 37ms when the enemy is 11765 µs away.
  • We don’t want it to happen at all if the enemy is further than that.

We can use the map() function to map the value of echoDuration to the values min and max value delay duration value in ms, and do it know only if echoDuration is bigger than our set distance.

if (echoDuration < maxEcho) {
    echoDuration = map(echoDuration, minEcho, maxEcho, 1, 37);

    for (pos = minAngle; pos <= maxAngle; pos += 1) {
      // in steps of 1 degree
      myservo.write(pos);
      delay(echoDuration);
    }
    for (pos = maxAngle; pos >= minAngle; pos -= 1) {
      myservo.write(pos);
      delay(echoDuration);
    }
}

And it works!

I’m surprised it works as there’s a delay of 60ms at the end of the loop which I thought was gonna make everything slow, but I guess not actually, it just means every 60ms there’s a small measurement happening… but doesn’t the for loop inside the loop() make the loop go on indefinitely?

I called this project Capitulate.

Download Capitulate.ino

Have you?