This is the project version of my talk "learn arduino by playing with fire." Slides from the talk are available here.
This project is the first project in a series. In this project, we set up the hardware that we will be using to teach Arduino coding concepts. In this first project we'll wire up the solenoid to a breadboard and test it.
We won't be shooting off any fire yet, but hold tight - we'll get there soon.
PrerequisitesI recommend taking Shawn Hymell's Udemy course, Arduino Programming and Hardware Fundamentals, prior to this tutorial series. This project assumes familiarity with the following concepts:
- Basic arduino programming; using the Arduino IDE to create sketches
- Reading schematics
- Reading a data sheet
- Ohm's law R (resistance) = V (voltage) / I (current)
- Watt's law P (watts) = I (current) * V (voltage)
- Using a multimeter
- Pulse width modulation
- Familiarity with components like LEDs, transistors, diodes, resistors and sensors
- How a solenoid works
- Using a flyback diode
- Using a transistor as a switch
- Using an N-channel MOSFET as a switch
- Debouncing buttons
We will be controlling the flow of fire in our poofer with a solenoid.
A solenoid is basically a coil of wire around a magnetic plunger. It uses electromagnetic induction to pull a magnetic plunger towards itself.
When the coil of wire is energized, it creates a magnetic field in its center, which causes the magnetic plunger to move towards it, closing or opening the valve, depending on whether the solenoid is "normally open" (NO) or "normally closed" (NC).
The solenoid we have selected is a plastic NC solenoid for use with pressurized gas.
Choosing a power supplyWe'll be controlling the solenoid from the Teensy 3.1, but the Teensy only supplies power at 3.3 volts and 5 volts. The solenoid I bought is rated for 4.8 watts of power at 12 volts, so we'll need an external 12 Volt power supply. How many amps must my power supply provide? Using Watt's law, we get:
4.8 Watts / 12 Volts = 0.4 Amps
It must supply at least 0.4 amps.
A 12 Volt power supply that supplies more than 0.4 amps is fine too. In fact, later we'll be powering our pump and LEDs off of the same 12 volt power supply. The pump takes 0.3 amps, so factor that in when picking out a power supply.
Also factor in the fact that your solenoid will be pushing against pressurized gas when the project is complete. Depending on the pressure it must push against, the solenoid may use more current. Solenoids take a lot of power. Don't be surprised if your solenoid gets hot while you're using it. This is normal and safe (just don't touch it while it's in use).
Note: A 5 amp power supply will not force 5 amps through anything you connect to it. It will only allow up to a maximum of 5 amps to flow. How much actually flows depends on the power supply voltage and the effective total resistance of the circuit connected to it.
Attach your 12 volt power supply's power to one rail of the breadboard and ground to the other to make a "12 volt rail" on your breadboard.
Connect the solenoid to the power supply
Crimp two female spade connectors onto two jumper cables, one red and one black. You can then easily connect the spade connectors to the two terminals of your solenoid. For this solenoid, it doesn't really matter which terminals you connect power ground to, since the plunger is simply a piece of magnetic metal, and doesn't have a magnetic polarity.
Plug the the positive terminal of your solenoid into the power rail, and the negative to the ground rail. You should hear it click open. Unplug the solenoid. You should hear it click close.
Controlling the solenoid from your microcontrollerWe'll be setting up a simple test circuit to test our solenoid from the Teensy.
The Teensy 3 sends only 3.3 volts when you write HIGH to a digital pin. Obviously we can't directly connect the 12 volt solenoid to the Teensy's GPIO. Instead, we'll be using a transistor as a switch.
The Solenoid test circuit comprises:
- Breadboard
- Teensy 3.1
- 12V Power Supply (to the breadboard's rail)
- Solenoid (positive pin to rail, negative pin to collector of Transistor)
- Transistor
- Flyback Diode (to collector rail)
- 220 ohm resistor between transistor base and Teensy
Using a transistor as a switch
We'll be using a transistor as a switch to turn on the solenoid solenoid. When the teensy sends the base HIGH from pin 6, it allows current to run from the collector to the emitter, closing the 12 volt circuit and turning the solenoid on.
Choosing a transistorIf we use a bipolar transistor, how much current will we need from our Teensy pin to saturate the base of the transistor and activate the solenoid?
For most bipolar transistors, divide the current that you want from the emitter by 10 or 20. That is the current needed on the base to fully saturate. So if I want 400 mA on the emitter I need 40 mA on the base.
The numbers 10 or 20 are the transistor's current gain, or "beta." There are higher "beta" transistors. Darlington transistors have higher betas, since they double the effect of a single transistor by running two collectors together. If you search for 'superbeta' transistors, you'll find ones that have betas of 40 or 50. With a transistor with a beta of 50, you would only need 8mA from the Teensy to the base.
Note: You can find exact values for your transistor's current gain, or "beta" if you search for "hFE" on the datasheet. You'll typically see this graphed, since the current gain decreases when more current is applied.
There's even an hFE setting on many multimeters.
How much current can the Teensy pins output?
Most chips have a "recommended maximum" and an "absolute maximum" per-pin current. It's good practice to avoid going over the recommended max. You can find a list of the maximum currents for a variety of Arduinos here, while the datasheets for the chips used on Teensy are available here.
Search for 'Voltage and Current Operating Ratings" on the Freescale datasheet. You'll find that for chips like the Teensy, it's 9 mA recommended max and 25 mA absolute max.
In order to use a bipolar transistor here, it would need a beta of 50.
Instead of a bipolar transistor, the simplest solution is to use a logic level N-channel MOSFET.
Unlike bipolar transistors, which are current controlled, MOSFETs are voltage controlled.
Because of this property, FETs are great for large current flow, and the MOSFET is commonly used as a switch. You'll still need a resistor between the MOSFET's gate and the Teensy, since the MOSFET's gate is highly capacitive and can draw a large amount of current when you switch it on.
A MOSFET may be thought of as a variable resistor, where the Gate-Source voltage difference can control the Drain-Source Resistance. When there is no applying voltage between the Gate-Source , the Drain-Source resistance is very high, which is almost like a open circuit, so no current may flow through the Drain-Source.
Logic level MOSFETs have a low "voltage threshold" needed to turn them on. Since the Teensy 3.2 operates at only 3.3 volts, we're using a MOSFET with a voltage threshold of 2.5 volts.
[add fritzing diagram here]
Wire the following:
- source -> ground of the 12 volt rail
- drain -> solenoid ground
- gate -> to 220om resistor attached to pin 6 of the teensy (pin number doesn't really matter)
Additionally, connect the solenoid power pin to 12 volt power rail, and ground of the arduino to the 12 volt ground rail. Now we're almost ready to test our solenoid in action.
But wait - we need one more important part!
The flyback diodeWhen you apply power to your solenoid to open it, the coils fill with current, turning into an electromagnet and pulling on the magnetic plunger.
When you cut power to the solenoid, the electromagnet continues to draw electricity, creating a large negative voltage where there used to be a positive voltage. This current has to go somewhere. Without a bypass diode, the current from the magnet will run backwards through our transistor and into our microcontroller, damaging it.
[make an animation that demonstrates this]
Note: If you have a solenoid with a plunger like this one, you can see this effect using a multimeter. Attach one of the solenoid's leads to the ground probe of the multimeter and the other to power. Turn the multimeter on amperage mode. Push the plunger in and release it and see what happens.
To fix this, we have a diode in parallel with the DC motor.
This diode is known as a flyback or bypass diode.
When the transistor is on, the current flows normally through the solenoid. When we turn off the transistor, the motor continues to draw current, but now it is redirected so that it doesn't pass dangerous voltages back through the transistor. This current loop, out the negative pin of the solenoid, through the diode, and back through the positive loop of the solenoid, will continue until all of the current has dispersed from the coils and is dissipated by the diode and resistance in the wires. This only takes a few milliseconds.
Schottky diodes, like the ones we're using, are preferred in flyback diode applications for switching power converters, because they have the lowest forward drop (~0.2 V rather than >0.7 V for low currents) and are able to quickly respond to reverse bias (when the solenoid is being powered from the transistor).
Test the solenoidNow that we have safety out of the way (phew), here is some code to test your valve:
//we're using #define instead of "const byte" or "const char" to save space.
//This is a best practice to adopt for constants
// when using define, it is customary to use all caps. you can identify it in the code that way.
#define SOLENOID = 6;
void setup() {
pinMode(SOLENOID, OUTPUT);
Serial.begin(9600);
}
void loop() {
digitalWrite(SOLENOID, HIGH); //opens solenoid
Serial.println("solenoid on"); //print to serial to let
//you know that it's working
delay(2000); //wait two seconds
digitalWrite(SOLENOID, LOW); //closes solenoid
Serial.println("solenoid off");
delay(2000);
}
Yup, opening and closing your solenoid is as simple as writing LOW or HIGH to pin 6, connected to the base pin, just like the basic blink sketch.
How about adding a simple push button to our circuit that holds the pump open when the button is pushed?
Here's the code:
#define BUTTON_PIN = 2; // the number of the pushbutton pin
#define TEST_LED = 13;
#define SOLENOID = 6;
void setup() {
pinMode(TEST_LED, OUTPUT);
pinMode(SOLENOID, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
}
void loop() {
// read the state of the pushbutton value:
int buttonState = digitalRead(BUTTON_PIN); // local variable for reading the pushbutton status
// check if the pushbutton is pressed. If it is, the buttonState is HIGH:
if (buttonState == HIGH) {
// turn LED on:
digitalWrite(TEST_LED, HIGH); //turns onboard LED on to test
digitalWrite(SOLENOID, HIGH); //opens solenoid
} else {
// turn LED off:
digitalWrite(TEST_LED, LOW);
digitalWrite(SOLENOID, LOW); //closes solenoid
}
}
If you test this out, you'll notice it's a bit buggy. We need to add a debounce.
When you push a button attached to a 3.3volt arduino, you might think it jumps right from ground to 3.3volts. In reality, the jump looks more like this:
In the first few milliseconds after the initial high reading, the button's voltage may jump around. One way to make up for this is by checking when the button is first pushed, and then waiting a few milliseconds before checking the value again. If you go to "file">"examples">"digital" and select "debounce" you'll see one way of doing this.
Here I've rewritten it for our solenoid:
#define BUTTON_PIN = 2;
#define TEST_LED = 13;
#define SOLENOID = 6;
//keeps track of the last time the output pin was toggled
unsignedLong lastDebounceTime = 0;
unsignedLong currentTime;
//this variable tracks the state of the button,
//low if not pressed, high if pressed
int buttonState = LOW;
// the debounce interval; increase if the output flickers
int debounceInterval = 50;
//this variable tracks the state of the solenoid,
//negative if off, positive if on
int solenoidState = -1;
void setup()
{
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(SOLENOID, OUTPUT);
pinMode(TEST_LED, OUTPUT);
}
void loop()
{
//current time is the actual time since the loop started
currentTime = millis();
// read the state of the switch into a local variable:
buttonState = digitalRead(BUTTON_PIN);
//filter out any noise by setting a time buffer
if ((currentTime - lastDebounceTime) > debounceInterval)
{
//if the button has been pressed, lets toggle the solenoid
if (( buttonState == HIGH ) && ( solenoidState < 0 )) {
digitalWrite(SOLENOID, HIGH);
//now the solenoid is on, we need to change the state
solenoidState = -soleonoidState;
//the button was just debounced, so the last debounce time is now
lastDebounceTime = currentTime;
}
else if (( buttonState == HIGH ) && ( solenoidState > 0 ))
{
digitalWrite(SOLENOID, LOW);
solenoidState = -solenoidState;
lastDebounceTime = currentTime;
}
}
}
This debouncing is done by creating a state machine. A state machine has variable to keep track of whether the button is on or off (the button's state), and variables to keep track of when the last change happened. If the button's state has changed, and a certain time interval has passed, we accept that the button has been pushed. This is an excellent way to filter out the noise.
The problem with using a state machine for debouncing is that you have to duplicate your code each time you add a new button, which we'll take a look at in our next tutorial.
Comments