Output Devices
group assignment see here
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
This week the only thing to download is the source code of the examples below.
Download source code filesThis 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.
I have the standard SG90 servo motor that comes with many starter packs. I found its very simple datasheet here.
… 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.
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()
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).
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
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
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.
I decided to measure the voltage as it is never exactly 5V… here it is 5.08V.
I would like to propose this as my group assignment as I'm doing what has been required, just not in a group. It's quite a simple task, I don't see why we need to be 10 to do that.
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.
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.
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 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:
Things they tell but we can figure out ourselves (and will):
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.
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.
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.
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…
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.
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?
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.
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.
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 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.
This will be the mother of all motherboards (in my project). We will plug in the mini-sensor-board to it which will give us values for soil moisture and light. It’ll then send the data over WiFi to a RPi which hosts a website to access the values and control the system.
The idea is that over time you can add more and more stuff to it, such as presence detection, tell you when coffee is ready and much more. (Wow.)
Right now I am planning to use multiple devices which we need to take into account for the design.
The Neopixel and ultrasonic sensors work at 5V, the water pump takes 12V and the ESP32 at 3.3V.
All these different voltages means we need to make a power board for powering the different devices.
The ESP32 “talks” at 3.3V (logic level), while the neopixels and ultrasonic sensor work at 5V. This means I’ll also need to integrate a level shifter, which allows for them to talk to each other.
I designed a little power board which takes in 12V DC through a jack. It then directly connects to a buck converter to bring it down to 5V (the output voltage is adjustable with a little screw).
The positive also goes to the positive of the pump, while the negative / ground goes to the source of a mosfet.
The main board is pretty simple. I inspired myself by the Barduino that was designed by the instructors in Barcelona.
The only thing I did is that I integrated a level shifter to it, and that’s it.
It still has the reset button, the program switch, the builtin LED and the FTDI connection.
In this article it explains how to build the level shifter they are selling.
It’s nice because it is bidirectional. I basically copied the design but did 3 level shifting, one for the Neopixel data line and two for the ultrasonic sensor’s trigger and echo pins.
The logic level conversion is accomplished with a simple circuit consisting of a single n-channel MOSFET and a pair of 10 kΩ pull-up resistors for each channel. When the input from the ESP32 (Lx) is driven low, the MOSFET turns on and the zero passes through to the Neopixel / Ultrasonic sensor (Hx). When Hx is driven low, Lx is also driven low through the MOSFET’s body diode, at which point the MOSFET turns on. In all other cases, both Lx and Hx are pulled high to their respective logic supply voltages.
I have a 12V jack input, which I labelled 12V and PW_GND (I didn’t name it GND because this is a separate board to the main board, but I made it in the same Eeschema file).
I'm not sure why I had the DMOSFET label connecting the two wires that are just next to teach other...
It then also goes to the + and - terminals of the buck converter which spits out 5V for the main board and sensors.
The more complex part was putting the NMOSFET together. The 12V+ of the jack goes straight to the pump whereas the 12V- goes to the SOURCE of the MOSFET.
To open or close the MOSFET I connected the pin 23 from ESP32 to the GATE of the MOSFET
The DRAIN is connected to the negative terminal of the pump.
That means that when I pull PIN23 to low on the ESP32, it closes the MOSFET and therefore closes the circuit and switches on the pump.
When the pump is turned on it starts spinning, but when it’s off it will generate electricity until it stops spinning. We need to make sure that electricity has somewhere to go, so we add a diode between the terminals of the pump. That creates a loop for the surplus energy to go.
The final design of the schematics.
Tool to calculate the track width depending on the current it will take.
I didn’t document this part because it’s just a matter of moving around stuff for ages until it all fits. There’s no better way of doing it, just do it. I’ve also documented in Electronics Design week.
Few tips and tricks and can make the job easier, for example, shortcuts:
Thanks to Oscar and his genius script you can export and prepare the files ready for mods in no-time.
Then used my documentation to create the RML files.
That is some good looking boards, I'm a proud papa.
I mean, this is easy. See further below, or eletronics production or electronics design or input devices.
I made a list of the components to solder. Copy and pasted it from Kicad rather than writing it by hand.
I went shopping :)
Again a proud papa, they look good! (and upside down)
I uploaded a simple blink program and it worked, so that’s good.
I checked and the soil moisture works fine with the potentiometer but it seems like the phototransistor isn’t working.
I used the torch on my phone and put it right up close to the phototransistor and it didn’t detect anything.
The problem is the choice of phototransistor, I was using the DIGIKEY PT15-21B/TR8, and then changed it to PT100MCOMP. It now works.
The first one was to detect infrared. The second one was for the entire color spectrum.
The power board didn’t work. It seems the tiny mosfet burnt out straight away without even powering the pump.
I think that’s the problem because I checked all the continuity, made sure 12V was being fed with a multimeter (it was) and the only part where it failed was the connectors of the pump.
We measured that the current was pulled HIGH to open the mosfet (it did, it showed 3V3), but the current wasn’t going through (and so the pump was being turned on). Not a massive surprise though, because we were expecting the mosfet to burn out as I accidentally picked the one for level shifter rather than power handling, which handles significantly less current.
Instead of re-milling the entire board I just soldered wires going up in the air, and then soldered the proper, bigger, stronger MOSFET.
I love how messy it is. I call it my Frankenstein board monster.
It still didn’t work though. After a lot of reviewing, searching, Oscar and I discovered that the footprint on Kicad was wrong, and the Gate, Drain and Source pins were mixed up.
Ultimately that meant that the actual design of the board was wrong, so I had to move traces around. Luckily the board has space and it wasn’t complicated to re-route the traces.
After redesigning, remilling and resoldering the board quickly (feat done in 50 mins, proof of my mastery of the process), it all worked as planned!
Edu, the master maker of Fab Academy, asked if I could send him the design of this little power board. That made me proud (damn, I've been proud at least 3 times on this page... I love electronics).
I'm quite happy with this board because it takes in 12V (although it could be more), converts it to 5V to power an Arduino (or 3V3 for an ESP32). That microcontroller can then control the MOSFET on the power board, switching on and off any high power device, in my case a water pump.
Article that tells which pins you can or can't use on ESP32
Pins 9, 10 & 11 seem to always be up as they activate the pump when you touch the traces although I didn’t set them to high.
Pin 1 (TX) seems to always be high as well.
Note //
I was using pins 7 and 8 to use as high to activate but it didn’t seem to work.
In the end I’m using:
First of all you need to know when the plants need to be watered. For that you can measure the capacitance of a pot and calibrate with the max level of water with two nails plugged in.
This is a very simple device that can easily be designed. I’m going to call it… the mini-sensor-board. Very creative.
To measure how moist a material is, in this case the soil, you can measure it’s conductivity. That is the property or power of conducting heat, electricity, or sound. Water is conductive, therefore measuring how conductive the soil is tells you how wet it is.
To measure it’s conductivity you simply hook one nail to a pin acting as a VCC and another nail to ground. You can measure the voltage between the nail and ground with an ADC pin on the ESP32 (see sketch below).
Note //
The first comment advises to use a digital output pin instead of the 5V / VCC, as you are able to switch it on only when you need it. That means it will consume less current, but also it keeps the nails from oxidising too quickly.
I later added a potentiometer between the analog pin on the ESP32 and the nail. That allows to change the resistance value and therefore tune the quality of the reading.
Note //
Remember, only ADC1 pins can be used when WiFi is used.
The very simple design is finalised. Let’s now design in Kicad.
Note //
I also decided to add a phototransistor to the mini-sensor-board as it would allow me to associate data about light (direct sunlight for example) and the dryness of the soil. I didn’t go through the schematics of designing it as it’s literally the same.
As you can see I added two potentiometers between the ground. I added one that was in the Kicad library. The footprint is most probably the wrong one. We’ll deal with that later.
Note //
I labeled it SENS_GND as the design for this mini-sensor-board is part of the Kicad file for the design of the motherboard, and the power board for that matter. We want them to have different GNDs as they SEPARATE BOARDS. Otherwise it would get confusing in PCBeditor.
The two pins where the nails will be soldered on are standard pin holes (not sure how they are called?).
For some reason the fab lab doesn’t really have potentiometers… the ones we had were leftover from other projects, but they were all enormous (physically) and the ones that were of a good size had a max resistance that was way to high (around 1MΩ). That meant that I only had a 1% of resolution.
In the end I am using some potentiometer I got with some Arduino kits. I’m not really happy because they are super big when I’d prefer to have a board that’s the smallest possible, but it’ll do for now. Their max resistance is 10kΩ which is good for this case.
Quite a simple board to put together, but as expected the footprint that came with the potentiometer symbol was wrong. It’s not a massive problem as I just had to create a new footprint which has the standard 3 pin holes in it and add a border around it.
Then connecting the few components together and here we have our mini-sensor-board.
I added through holes in the corners of the board to be able to screw it to a casing. They are via holes with a diameter of 3.6mm as the screws are 3mm.
Oscar made an awesome tool for exporting the different layers to SVG, and the use the Inkscape CLI to convert to PNGs, ready to be sent to mods for creating the G-code files.
The documentation Oscar wrote for this works perfectly and is very well written, it would be an insult to paraphrase it here.
We went through how to mill a board a few times already, not sure if we need to again… Ok, let’s do it.
We now have the PNGs to mill the board. I followed this tutorial, and made some adjustments.
By default the X, Y and Z axis have an offset value for their origin in the Roland SRM-20 milling machine module.
We realised this when I launched the milling and it was spinning a few mm above the PCB board.I went back to the Mods and changed the origin to 0, 0, 0.
Below is a picture of the 1/32” and 1/64” end-mills. The thinnest one, 1/64”, is used to mill outside the traces. It has a higher resolution so that it can mill thin lines in between the traces to go to the ATtiny44.
Before any milling you need to insert the appropriate end-mill for the task, and set the X/Y axis as well as the Z axis.
To do so, insert the tool, make sure it is tightly screwed so that it doesn’t lower as you mill. Move the tool so that it is a safe amount above the PCB board (something like 50mm), and move with Continuous mode towards the origin point of your board (which you decide, but usually further bottom-left possible). When you get near it, you change the Cursor Step to x100, x10 and x1 respectively.
Changing the speed mode is most important when lowering the tool on the Z-axis, so that it doesn’t crash into the board and possibly break this fragile end-mills.
Lower the end-mill slowly until it touches, or almost does, the board. Unscrew the end-mill and let it rest on the board, pressing on the board a tiiiny bit while screwing back on the end-mill. This means the end mill will be slightly pressing on the board.
Click on the ON under the Spindle menu on the left after having set the Cut Speed at 40% to check that the Z is at the right depth. It should create a tiny bit of dust.
If you are happy with it, press Set Z.
After having set the X/Y & Z origins, reset the Cut Speed to 100% and having inserted the 1/64” end-mill, click on Cut, make sure to select the right file and click Output to launch the milling process.
Ok let’s do the outlines. Replace the endmill with the 1/64” one and make sure TO SET THE Z again.
Cut > Add > hello.ISP.44.interior.png.rml and click Output.