Sunday, March 29, 2015

[Finished!] Wii Remote + Arduino + LEGO RC Car (Part 4): Controlling DC Motors with the Wii Remote

Table of contents
  1. Wii Remote + Arduino + LEGO RC Car (Part 1): Parts Selection
  2. Wii Remote + Arduino + LEGO RC Car (Part 2): Driving DC Motors
  3. Wii Remote + Arduino + LEGO RC Car (Part 3): Bluetooth Dongle via USB Host Shield
  4. [Finished!] Wii Remote + Arduino + LEGO RC Car (Part 4): Controlling DC Motors with the Wii Remote



We've made it to the final part of the RC car build. Probably.



Here's the completed circuit:



Completed RC car circuit diagram



It looks complicated, but it's just everything from previous parts combined.
Let's walk through it piece by piece.



Motor Driver TA7291P


Check on Amazon
We need to control the left and right motors independently,
so we place two motor drivers in a mirror-symmetric layout.



Unlike before, this time we're connecting three pins per driver to the Arduino — no fixed resistor.



This wasn't a random change. There's a real reason for it.
(…which I realised only after assembling everything.)



USB Host Shield and Pin Constraints


The reason for dropping the fixed resistor comes down to the combined constraints of the USB Host Shield, Arduino UNO, and TA7291P.



Arduino controls output voltage through PWM, but not every pin supports PWM.
Only pins marked with ~ on the board support it: specifically D3, D5, D6, D9, D10, D11.



When the USB Host Shield is stacked on the Arduino UNO, it occupies D7–D13,
leaving those pins unavailable for motor control.



Of the remaining D0–D6, only D3, D5, and D6 support PWM — just three pins.



With a fixed resistor, controlling the TA7291P requires one PWM pin per direction (forward on pin 5, reverse on pin 6), so two PWM pins per driver.
Two drivers means four PWM pins — but we only have three. Not enough.



Instead, we use three pins per driver (pins 4, 5, 6) with no fixed resistor.
In this configuration, pin 4 handles speed via PWM, while pins 5 and 6 just set direction (HIGH/LOW).
Two drivers now need only two PWM pins and four digital pins — exactly what we have available.



With the USB Host Shield mounted, we have D3, D5, D6 for PWM and D0, D1, D2, D4 for digital — just barely enough.



Note: if you only need forward/back/spin-in-place without variable speed, you don't need PWM at all and can skip this whole constraint puzzle.



Motor Direction and Track Movements


Tracks (as opposed to wheels) have a special move: spin in place.
Here's how motor directions map to vehicle movement:


  • Forward

    Left motor: forward   Right motor: forward

  • Reverse

    Left motor: reverse   Right motor: reverse

  • Turn left

    Left motor: forward (reduced speed)   Right motor: forward

  • Turn right

    Left motor: forward   Right motor: forward (reduced speed)

  • Reverse left

    Left motor: reverse (reduced speed)   Right motor: reverse

  • Reverse right

    Left motor: reverse   Right motor: reverse (reduced speed)

  • Spin left (in place)

    Left motor: reverse   Right motor: forward

  • Spin right (in place)

    Left motor: forward   Right motor: reverse



Program


The D-pad on the Wii Remote controls the track direction.
Tilting the remote left or right (held horizontally) adjusts the turning angle.



Here's the sketch to upload to the Arduino UNO:



Fair warning: I accidentally lost the original sketch from the video.
The code below was written fresh for this post and hasn't been tested on the actual hardware.
Let me know if you spot any bugs.

#include <Wii.h>
#include <usbhub.h>

#define  R_INR  3
#define  R_IN1  1
#define  R_IN2  2
#define  L_INR  6
#define  L_IN1  4
#define  L_IN2  5

USB Usb;
USBHub Hub1(&Usb);
BTD Btd(&Usb);
WII Wii(&Btd,PAIR);

void setup() {
Serial.begin(115200);

// Wait for Wii Remote connection
if (Usb.Init() == -1) {
Serial.print(F("\r\nOSC did not start"));
while(1); //halt
}
Serial.print(F("\r\nWiimote Bluetooth Library Started"));

// Set Arduino UNO output pins
pinMode(R_INR, OUTPUT);
pinMode(R_IN1, OUTPUT);
pinMode(R_IN2, OUTPUT);
pinMode(L_INR, OUTPUT);
pinMode(L_IN1, OUTPUT);
pinMode(L_IN2, OUTPUT);
}

void loop() {
Usb.Task();

// Stop if Wii Remote disconnects
if(!Wii.wiimoteConnected) { return; }
if(Wii.getButtonClick(HOME)) {
Serial.println("CLOSE");
Wii.disconnect();
return;
}

// Get tilt angle of horizontally-held remote (0-360)
int pitch = Wii.getPitch();
int right = 0;
int left = 0;
if( 0 <= pitch && pitch <= 90 ){
right = constrain( map(pitch, 0, 45, 0, 255), 0, 255 );
}else if( -90 <= pitch && pitch <= 0 ){
left = constrain( map(pitch, 0,-45, 0, 255), 0, 255 );
}else if( 270 <= pitch ){
left = constrain( map(pitch, 360, 315, 0, 255), 0, 255 );
}

if(Wii.getButtonPress(UP)){
// UP button: both motors forward
digitalWrite(R_IN1, HIGH);
digitalWrite(R_IN2, LOW);
digitalWrite(L_IN1, HIGH);
digitalWrite(L_IN2, LOW);

// Tilt right: slow down right; tilt left: slow down left
analogWrite(R_INR,255 - right);
analogWrite(L_INR,255 - left);
}else if(Wii.getButtonPress(DOWN)){
// DOWN button: both motors reverse
digitalWrite(R_IN1, LOW);
digitalWrite(R_IN2, HIGH);
digitalWrite(L_IN1, LOW);
digitalWrite(L_IN2, HIGH);

// Tilt right: slow down right; tilt left: slow down left
analogWrite(R_INR,255 - right);
analogWrite(L_INR,255 - left);
}else if(Wii.getButtonPress(RIGHT)){
// RIGHT button: spin right (reverse right motor only)
digitalWrite(R_IN1, LOW);
digitalWrite(R_IN2, HIGH);
digitalWrite(L_IN1, HIGH);
digitalWrite(L_IN2, LOW);
analogWrite(R_INR,255);
analogWrite(L_INR,255);
}else if(Wii.getButtonPress(LEFT)){
// LEFT button: spin left (reverse left motor only)
digitalWrite(R_IN1, HIGH);
digitalWrite(R_IN2, LOW);
digitalWrite(L_IN1, LOW);
digitalWrite(L_IN2, HIGH);
analogWrite(R_INR, 255);
analogWrite(L_INR, 255);
} else {
// No button: stop
digitalWrite(R_IN1, LOW);
digitalWrite(R_IN2, LOW);
digitalWrite(L_IN1, LOW);
digitalWrite(L_IN2, LOW);
analogWrite(R_INR, 0);
analogWrite(L_INR, 0);
}
}



LEGO Assembly


Check on Amazon
Build the LEGO however you like.

The one issue: the shaft coming out of the gearbox doesn't match the axle hole size on the LEGO track gear.



The easiest fixes are to fill the hole with hot glue, or to shave down a pinion gear from a Tamiya Mini 4WD kit.




And that's the RC car finished.

Hopefully this showed just how much you can build with ~100 lines of Arduino code — a fully wireless remote-controlled vehicle.



Mounting a phone on top for a camera car is also a lot of fun.






I built this to play with my kids.
They completely ignored it. Please do not ask about this.


I later 3D-printed an adapter to connect the gearbox shaft to the LEGO gear.



Source code is available on GitHub.

No comments:

Post a Comment