In my previous guides (HERE and HERE), I gave the instructions to control easily a brushed motor (into a single direction) and a servo with uChip and few external components. However, some applications require driving a motor in both directions.
How is it possible to drive a brushed motor both clockwise/counter clockwise, or equivalently, forward/reverse?
The answer is FULL BRIDGE!
There are several commercially available Electronic Speed Controllers (ESCs) both for brushed and brushless motors. These speed controllers are specifically made to minimize the noise generated by switching current/voltage over an inductive load (the motor), and are generally designed to endure very high current/voltage spikes. ESCs usually integrate a microcontroller (MCU) specifically dedicated to handle all the tasks necessary to optimize efficiency and avoid forbidden states that would burn the ESC itself.
However, if you are knowledge hungry and willingly to make your own full bridge, it is possible to build an ESC for brushed motors with few components and some code, using the rather simple Arduino IDE and its libraries.
This tutorial will show you how!
I am going to explain how to build your own ESC for brushed motors using uChip and few external components. Furthermore, besides the ESC capabilities, I added into uChip the necessary code (reuse of an old sketch) to drive a servo and acquire the cPPM Radio signal coming from the radio Rx. uChip features a Cortex M0+, undertaking all of this is an easy task!
All this allows me to transform any old toy into an RC toy, and the first test subject to undertake this mutation is a LEGO® motorbike I built as an example.
I designed the code provided in this tutorial specifically on the SAMD21 MCU and uChip PINOUT.
This means that you can easily adapt the code to any Arduino compatible board featuring the same microcontroller (e.i. Arduino Zero equivalent board). However, since I optimized the code to use all the available timers based on uChip pinout, you must be very careful in case you switch to another board and you must re-do properly the wiring accordingly to the chosen board.
In order to make the correct wiring, check the “variant.cpp” file of your board. Reassign the pins on your board matching the port number corresponding to uChip pin (as an example, uChip PIN_10 connects to PORTA22 of the MCU, which is PIN_9 of the Arduino Zero. Correspondingly, in the code you need to change uChip PIN_10 into Arduino Zero PIN_9).
FAILING TO WIRE CORRECTLY YOUR BOARD MAY RESULT IN SEVERE DAMAGE TO THE EXTERNAL ELECTRONICS AND/OR YOUR DEV BOARD! This is one of the risks you undertake when developing your own full bridge :)
Bill of materials:
- 1 x uChip: Arduino IDE compatible board
- 1 x Tx-Rx Radio system: any radio system with cPPM receiver is good my combo is Spectrum DX7 Radio & Orange 614XN receiver
- 1 x proto-board/breadboard: this is necessary to solder/connect all the components together.
- 2 x nMOSFET + 2 x pMOSFET: specs are VGTH ~ 1÷2V, IDC ~ 5A, VDS ~ 20V
- 2 x NPN BJT: standard BC337 or equivalent signal NPN BJT
- 2 x 33kΩ + 2 x 10kΩ + 4 x 3.3kΩresistors
- 1 x Zener 5V1 Diode: specs are VZ = 5.1V, PDISS ≥ 1W (2W recommended)
- 1 x 47µF (electrolytic) + 1 x 100nF (ceramic) capacitors: specs 16÷25V max voltage rating
- 1 x Zener 3V3 Diode: specs are VZ = 3.3V, signal zener diode (optional)
- 1 x Schottky Diode: specs PDISS ≥ 1W, power diode (optional)
- 1 x 3Header + 1 x 2Header connectors: to attach the servo and motor, otherwise just solder directly the wires (optional)
- 1 x Li-ion 18650 battery: you can recycle one from an old notebook battery pack, I suggest using a polarized connector to connect the battery in order to avoid erroneous reverse connection.
- 1 x Motor: use a motor with a maximum stall current of 1÷2A. In case you use motors with higher current absorption you need to change the electronics!
- 1 x Servo: a 9g servo is more than enough to drive a small toy. In case you need more steering power change your servo accordingly. BE CAREFUL TO THE SERVO VOLTAGE OVERSHOOT! The bigger the servo the higher the overshoot on the 5V pin of the servo, therefore you need a higher power 5V1 Zener diode in order to clamp the overshoot without burning the diode.
In order to reproduce successfully the example provided in this tutorial, I heartily suggest you to adopt this bill of material and correctly reproduce the schematic. I can’t predict what would happen with other components or wiring.
Step 1: Build the Frame
Make yours! I used an old LEGO® kit to build mine but you can recycle an old toy that suits your purposes.
Step 2: Make the Electronics
When it comes to drive a brushed motor in both directions, the only solution is using a FULL BRIDGE (brushless motors make a league of their own!).
The schematic attached to this tutorial illustrates how to connect the external electrical components listed in the bill of materials to uChip. Be careful not to confuse pMOS/nMOS pinout and proper connect the diodes or they won’t operate correctly by clamping the overshoots (your circuit/dev board will be exposed to serious damage otherwise!)
If you already know (or you don’t want to know) how a full bridge works, you can simply skip the following paragraph and go to the "Coding" step. Otherwise, keep reading to become a full bridge expert!
What is a full bridge?!
Have a look at the schematics to get a better grasp of what I will try to explain here.
A full bridge is made by 2 half bridges (called branch A and B in the schematic), each one made of 2 switches, one (HIGH side) connecting the mid node to the power supply positive terminal (VBAT+), while the other (LOW side) connecting the mid node to the negative power supply terminal (VBAT- or GND). Therefore, 4 switches are necessary to implement a full bridge (the 4 MOSFETs on the schematics), which turn on and off in order to redirect the power supply to the desired node. The remaining NPN BJT transistors and resistors represent the necessary logic circuitry to turn on/off the MOSFETs using uChip output pins.
A FULL BRIDGE can be set into 5 positions (4 usable and 1 forbidden), which are described in the attached pictures:
- FORWARD: current flows from A branch High side to B branch Low side. The motor spins.
- REVERSE: current flows from B branch High side to B branch Low side. The motor spins into the opposite direction compared to the forward state.
- BRAKE: current circulates through A and B branchs Low (or High) sides. This states short circuits the motor terminals, thus braking its spinning and slowing it down. The motor brakes.
- FREE WILLING: current circulates through the body diodes of the switches (which are all off). Current direction depends on the motor spinning direction. Since the current flows back into the power supply, this state allows free spinning of the motor without much resistance. However, it could generate high overshoots on the power supply due to the current flow. Therefore, the presence of the 5V1 Zener Diode is mandatory to avoid damaging the connected electronic components. In case you are planning to use this state, I recommend installing also the optional schottky diode as an additional protection.
- FORBIDDEN: current flows only on A (or B) single branch, this state actually short circuits the power supply. NEVER ENTER THE FORBIDDEN STATE, IT WILL BURN THE SWITCHES! YOU SHALL NOT PASS (cit.)!
Next question is: How do we control the motor speed?
The answer is Pulse Width Modulation (PWM), which the Arduino IDE hides behind the analogWrite() function applied to PWM enabled pins, as I will better explain into the "Coding" step.
Using PWM allows giving “pulsed” power to the motor. The longer the pulse, the longer the time we apply power to the motor. In case you chose the motor proportionally to your application, this should result in a higher speed. In case you are trying to move your car with an electric toothbrush motor, increasing the pulse length won’t increase the speed, since you will probably never exit the stall position of the motor!
In this tutorial, the code makes use of all the usable state: FORWARD, REVERSE, FREE WILLING and BRAKE. However, once you get confident with the states of a full bridge, you are more than welcome to try and experiment on your own all the usable states and find out which is the best combination for your project.
Step 3: Coding
It is now coding time!
In case you followed the guide step-by-step, load the sketch “MakeAnyToyIntoRCToy.ino” into uChip and connect it to the external electronics. If nothing burns, it means you are ready to assemble and play! You can skip the explanation below and go directly to the “Assembly and Enjoy!” step.
In case you have smoking MOSFETs, go back to the “Making the electronics” step and re-check the wiring procedure (replace the smoking MOSFETs). Make sure you assembled and soldered the schematic correctly before trying once more to power on the circuit.
No smoke but you want to understand how the code works?! Keep reading :)
The firmware handling the Rx cPPM signal and the servo are based on the sketch I previously developed, which you can find HERE. However, I modified the timer used (TC3 instead of TCC0) for cPPM reading. This slightly changes the code but gets the same results and was necessary to avoid undesired output on the PWM pins when using the “analog” Arduino IDE library.
In the loop() function, after channels update, I added the code that sets the motor and servo values accordingly to the sticks position. While the servo0_value is calculated directly from the tilt stick value, it is necessary some more math for the motorFB_value calculation. The attached picture describes the function that I implemented in the code (using some if..else and the “mapValue” and “fitChToValue” functions) to calculate the motor value to use when driving the half bridge.
When the throttle stick is in middle position (±HEADROOM), the motor has to be in brake position. Which means that I need to associate the “min MOTOR” of the graph to the BRAKE state.
On the other hand, when the stick is higher than the “Mid Range” value, the motor should proportionally rotate FORWARD and below the “Mid Range” it should proportionally rotate REVERSE. In case you need to switch the REVERSE and FORWARD states, don’t worry, it is possible to switch them later on the “motorFBwrite()” function by simply reversing the sign of the calculated motorFB_value.
The “mapValue” (using int) and “fitChToValue” (using uint16_t) functions also normalize the given value to the set MAX and min, thus adjusting the motorFB_value to the desired range.
Once I calculated the values of the servo and motor, I use them to change correspondingly the position and speed. The well-known “Servo” library allows easily generation of the servo-driving signal. While the function motorFBwrite() that I coded sets the half bridge in the desired state accordingly to the set motorFB_value.
In order to explain how the motorFBwrite() function works, you need to know what a PWM signal is. The tutorial provided by Arduino (HERE) explains it quite well. In the Arduino IDE environment it is possible to generate a PWM signal by using the analogWrite() function on a PWM enabled pin. This is what I use inside the motorFBwrite() function.
The BRAKE position is implemented by turning the full bridge High sides OFF (digitalWrite(PIN_A_H, LOW) & digitalWrite(PIN_B_H, LOW)) and setting the maximum PWM available (setting 255 as pulse length means that the pin is always HIGH) on the Low sides.
On the other hand, the FORWARD/REVERSE position is coded by turning ON the A/B High side and setting the B/A Low side to the desired motorFB_value pulse length (which was previously normalized to fit within the 0-255 range required by the analogWrite() function) by using analogWrite().
Mind that before setting any of the described combination, I reset all the switches OFF (FREE WILLING). This prevents putting the full bridge into the FORBIDDEN state!
Tune or adapt the code to your needs. Coding is fun; it just takes some time to learn the basics but it will make you feel great satisfaction!
You can comment/uncomment the DEBUG define in order to print on the SerialUSB the motors and channels values. This could be very useful in order to tune the min_range, mid_range and max_range accordingly to your Tx-Rx Radio system.
Step 4: Assembly and Enjoy!
Now simply integrate the powerful driving electronics into the chosen toy, and make it an RC toy!
In my case, I built a simple LEGO® motorcycle to test everything. You can see the result in the picture.
I definitely need to improve the electronics integration, however, as you can see in the video, now I can enjoy my LEGO® motorcycle as a remote controlled toy!