Help with programming a Ev3 RC car

Hi Wayne and Layne. I am currently 13 years old. I had recently bought two motor drivers from your website. my ultimate project is an RC car that is powered by Lego motors. Since I enjoy programming in the language Arduino, I came across your product of pretty much hacking the LEGO Ev3 with Arduino. I have been working on this project for a couple of weeks now I seemed to get a hang of it. I figured out how to take in pulses from the transmitter/receiver and map them to values -255 to 255. Then I downloaded your Brickstronics Master Library and tried to use a couple of functions from there to hopefully start turning motors. From what I understand, setFixedDrive() is a function that controls the speed and direction of the motor turn. So I decided to store the -255 to 255 value into a variable X and put it into the function setFixedDrive(X); hoping to control the speed and direction. I tried many different ways and yet none seem to work. To give you a better understanding of what happened, here is my code.

#include <BricktronicsMotor.h>

// Controller pins

// Motor driver pins
const int AIN1_PIN = 12; // enable motor steering
const int AIN2_PIN = 11; // forward/backward steering
const int BIN1_PIN = 10; // enable motor drive
const int BIN2_PIN = 9; // forward/backward drive
const int DRIVE_PWM_PIN = 8; // Output - Analog?
const int STEERING_PWM_PIN = 6; // Output - Analog?
const int STEERING_PIN = 4; // Arduino pin D12 receiver
const int DRIVING_PIN = 7; // Arduino pin D11 receiver
const int T1_PIN_DRIVE = 1; // for the time being (not connected)
const int T2_PIN_DRIVE = 2; // for the time being (not connected)
const int T1_PIN_STEER = 5; // Steering techonmitor pin 1
const int T2_PIN_STEER = 3; // Steering techonmitor pin 2
// Parameters
const int deadzone = 17; // Anything between -20 and 20 is stop

// Initialize values
// steering middle: 1504.5
// drive middle: 1501.5

int max_pulsein_steering = 0; // 1922 Use to track maximum limit of transmitter steering
int min_pulsein_steering = 3000;// 1087 Use to track minimum limit of transmitter steering
int max_pulsein_drive = 0; // 1917 Use to track maximum limit of transmitter throttle
int min_pulsein_drive = 3000; // 1086 Use to track minimum limit of transmitter throttle
int CURRENT_STEERING_clockwise_POSITION = 120;
int CURRENT_STEERING_counterclockwise_POSITION = 120;
bool FORWARD = 0;
int Speed = 0;

BricktronicsMotor m(BIN1_PIN, BIN2_PIN, DRIVE_PWM_PIN, T1_PIN_DRIVE, T2_PIN_DRIVE);

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

// Configure pins
pinMode(AIN1_PIN, OUTPUT);
pinMode(AIN2_PIN, OUTPUT);
pinMode(BIN2_PIN, OUTPUT);
pinMode(DRIVE_PWM_PIN, OUTPUT);
pinMode(STEERING_PWM_PIN, OUTPUT);
pinMode(STEERING_PIN, INPUT);
pinMode(DRIVING_PIN, INPUT);

m1.begin();
}

void loop() {

// Read pulse width from receiver
int futaba_ch_3 = pulseIn(DRIVING_PIN, HIGH, 25000);

// Convert to PWM value (-255 to 255)
int PWM_ch_3 = pulseToPWM255(futaba_ch_3);

Serial.print("CH_1_RAW: “);
Serial.print(futaba_ch_1);
Serial.print(” ");
Serial.print("CH_1_PWM: ");
Serial.println(PWM_ch_1);

// Drive motor
drive(PWM_ch_3);

delay(1);
}

// Convert RC pulse value to motor PWM value
int pulseToPWM255(int pulse) {

// If we’re receiving numbers, convert them to motor PWM
if ( pulse > 1000 ) {
pulse = map(pulse, 1093, 1904, -255, 255);
pulse = constrain(pulse, -255, 255);
} else {
pulse = 0;
}

// Anything in deadzone should stop the motor
if ( abs(pulse) <= deadzone ) {
pulse = 0;
}
return pulse;
}

void drive(int ch3_pulsein) {

m.setFixedDrive(ch3_pulsein);

}
}

That was my final code for driving the motors. When I ran this code, the motor acted as an on and off switch, as in, when I move the joystick of the controller up, the motor goes straight to the speed 255 and when I try to make the motor go backward, it just stays at -100 speed. I would like to see acceleration at different speeds, since this is something that matters significantly to me. My dad knows Java, and C++ but is not fluent in Arduino. He helped me a lot and we both agreed that the code should be working when it wasn’t. That’s why I came to ask you guys.
Now for the steering, it took far longer than I expected. I first tried again to map the pulsein to -120 to 120 (the position of the motor I would like it to go), then put it into the function, goToPosition();. It didn’t work out the way I wanted to. So I started to go much simpler by just asking the motor to go to one position with that function, but it never seemed to get there. I did more research on the function and found that it had something to do with PID. When searching, I found that PID was a type of machine learning for motors. So I realized that the functions wouldn’t work for my needs. So I tested it a different way by grabbing the position and trying to match the target position with the current position. Here is a simple code I tried.

#include <BricktronicsMotor.h>

//steering Test
const int VAR_255 = 255;
const int EN_PIN = 12;
const int DIR_PIN = 11;
const int PWM_PIN = 6;
const int T1_PIN = 5;
const int T2_PIN = 3;
int CURRENT_STEERING_clockwise_POSITION = 120;
int CURRENT_STEERING_counterclockwise_POSITION = 120;
int STEERING_PWM = 0;
int tempMax = -120;
int tempMin = 120;
int moving_position = 1;
int step_size = 1;
int increment = 1;
int increment2 = -1;
int step_size2 = 0;
int addOne = -1;
bool FORWARD = 0;
int zero = 0;
int tempnum = 0;
BricktronicsMotor m1(12, 11, 6, 5, 3);

void setup()
{
pinMode(EN_PIN, OUTPUT);
pinMode(DIR_PIN, OUTPUT);
pinMode(PWM_PIN, OUTPUT);
m1.begin();
delay(2000);
m1.setPosition(0);
Serial.begin(9600);

}

void loop(){

if (tempnum = 0) {
m1.setFixedDrive(255);
if (m1.getPosition() >= 5) {
m1.setFixedDrive(0);
m1.hold();
delay(1000);
tempnum = 1;
}
}

if (tempnum = 1) {
m1.setFixedDrive(-255);
if (m1.getPosition() <= -10) {
m1.setFixedDrive(0);
m1.hold();
delay(1000);
tempnum = 2;
}
}

if (tempnum = 2) {
m1.setFixedDrive(255);
if (m1.getPosition() >= 15) {
m1.setFixedDrive(0);
m1.hold();
delay(1000);
tempnum = 3;
}
}
if (tempnum = 3) {
m1.setFixedDrive(-255);
if (m1.getPosition() <= -20) {
m1.setFixedDrive(0);
m1.hold();
delay(1000);
tempnum = 4;
}
}
if (tempnum = 4) {
m1.setFixedDrive(255);
if (m1.getPosition() >= 25) {
m1.setFixedDrive(0);
m1.hold();
delay(1000);
tempnum = 5;
}
}
if (tempnum = 5) {
m1.setFixedDrive(-255);
if (m1.getPosition() <= -30) {
m1.setFixedDrive(0);
m1.hold();
delay(1000);
tempnum = 6;
}
}
if (tempnum = 6) {
m1.setFixedDrive(255);
if (m1.getPosition() >= 35) {
m1.setFixedDrive(0);
m1.hold();
delay(1000);
tempnum = 7;
}
}
if (tempnum = 7) {
m1.setFixedDrive(0);
m1.hold();
}
}

This was meant to run only once. Before this, I already put the drive and steering together. Here is my code.

#include <BricktronicsMotor.h>

// Controller pins

// Motor driver pins
const int AIN1_PIN = 12; // enable motor steering
const int AIN2_PIN = 11; // forward/backward steering
const int BIN1_PIN = 10; // enable motor drive
const int BIN2_PIN = 9; // forward/backward drive
const int DRIVE_PWM_PIN = 8; // Output - Analog?
const int STEERING_PWM_PIN = 6; // Output - Analog?
const int STEERING_PIN = 4; // Arduino pin D12 receiver
const int DRIVING_PIN = 7; // Arduino pin D11 receiver
const int T1_PIN_DRIVE = 1; // for the time being (not connected)
const int T2_PIN_DRIVE = 2; // for the time being (not connected)
const int T1_PIN_STEER = 5; // Steering techonmitor pin 1
const int T2_PIN_STEER = 3; // Steering techonmitor pin 2
// Parameters
const int deadzone = 17; // Anything between -20 and 20 is stop

// Initialize values
// steering middle: 1504.5
// drive middle: 1501.5

int max_pulsein_steering = 0; // 1922 Use to track maximum limit of transmitter steering
int min_pulsein_steering = 3000;// 1087 Use to track minimum limit of transmitter steering
int max_pulsein_drive = 0; // 1917 Use to track maximum limit of transmitter throttle
int min_pulsein_drive = 3000; // 1086 Use to track minimum limit of transmitter throttle
int CURRENT_STEERING_clockwise_POSITION = 120;
int CURRENT_STEERING_counterclockwise_POSITION = 120;
bool FORWARD = 0;
int Speed = 0;

BricktronicsMotor m(BIN1_PIN, BIN2_PIN, DRIVE_PWM_PIN, T1_PIN_DRIVE, T2_PIN_DRIVE);

BricktronicsMotor m1(AIN1_PIN, AIN2_PIN, STEERING_PWM_PIN, T1_PIN_STEER, T2_PIN_STEER);

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

// Configure pins
pinMode(AIN1_PIN, OUTPUT);
pinMode(AIN2_PIN, OUTPUT);
pinMode(BIN2_PIN, OUTPUT);
pinMode(DRIVE_PWM_PIN, OUTPUT);
pinMode(STEERING_PWM_PIN, OUTPUT);
pinMode(STEERING_PIN, INPUT);
pinMode(DRIVING_PIN, INPUT);

m1.begin();
delay(3000);
m1.setPosition(-120);
}

void loop() {

// Read pulse width from receiver
int futaba_ch_3 = pulseIn(DRIVING_PIN, HIGH, 25000);
int futaba_ch_1 = pulseIn(STEERING_PIN, HIGH, 25000);

// Convert to PWM value (-255 to 255)
int PWM_ch_3 = pulseToPWM255(futaba_ch_3);
int PWM_ch_1 = pulseToPWM240(futaba_ch_1);

Serial.print("CH_1_RAW: “);
Serial.print(futaba_ch_1);
Serial.print(” ");
Serial.print("CH_1_PWM: ");
Serial.println(PWM_ch_1);

// Drive motor
drive(PWM_ch_3);

int prev_position = m1.getPosition();

gottoposition(PWM_ch_1, prev_position);

delay(1);
}

// Convert RC pulse value to motor PWM value
int pulseToPWM255(int pulse) {

// If we’re receiving numbers, convert them to motor PWM
if ( pulse > 1000 ) {
pulse = map(pulse, 1093, 1904, -255, 255);
pulse = constrain(pulse, -255, 255);
} else {
pulse = 0;
}

// Anything in deadzone should stop the motor
if ( abs(pulse) <= deadzone ) {
pulse = 0;
}
return pulse;
}

int pulseToPWM240(int pulse2) {

// If we’re receiving numbers, convert them to motor PWM
if ( pulse2 > 1000 ) {
pulse2 = map(pulse2, 1093, 1904, -120, 120);
pulse2 = constrain(pulse2, -120, 120);
} else {
pulse2 = 0;
}

// Anything in deadzone should stop the motor
if ( abs(pulse2) <= 10 ) {
pulse2 = 0;
}
return pulse2;
}

// Positive for forward, negative for reverse
void drive(int ch3_pulsein) {

m.setFixedDrive(ch3_pulsein);

}

void gottoposition(int target_position, int prev_position ) {

if ((target_position - prev_position) < 0 ) {
FORWARD = false;
}
else if ((target_position - prev_position) > 0 ) {
FORWARD = true;
}
else {
m1.setFixedDrive(0);
m1.hold();
}

if (FORWARD = true) {
if (m1.getPosition() < target_position){
m1.setFixedDrive(255);
}
if (m1.getPosition() >= target_position){
m1.setFixedDrive(0);
m1.hold();
}
}
if (FORWARD = false) {
if (m1.getPosition() > target_position){
m1.setFixedDrive(-255);
}
if (m1.getPosition() <= target_position){
m1.setFixedDrive(0);
m1.hold();
}
}
}
I really had a lot of trouble programming this project and debated a long time whether to post this or not. I can only hope that someone took the time to read this post, but if you do, thanks a lot for you time.

Hi @zdragonite21! Welcome to the W&L forum! Thanks so much for your post, we definitely read it, but due to the length it’ll take a few days for us to make time to read and process it all to provide some specific suggestions. First we’ll respond with some quicker thoughts:

  1. My first recommendation for everyone, brand new folks to super-experienced folks, is to make sure the examples work for you. There are a lot of different things that work together to make all of this go, and if the examples don’t work like they say they should, we can start there with troubleshooting! My recommendation is to upload MotorSingle and read through it, and make sure it works like you think it should.
  2. Does your ultimate car steer by angling some wheels? If so, go over the MotorAngleControl example.
  3. PID is a way of controlling things in the real world. Imagine programming a robot to pick up a soda can. When it lifts the can up, it needs to take into account how much force it is using to pull up, and what the can is doing. If the can isn’t moving, it needs to pull harder. If the can is moving way too fast, it needs to slow down or the can is going to go flying! We handle the complicated bits of PID in the library, which lets you do things like tell the motors to “go to this position and don’t execute more code until you get there” or “start going to this position but let me do other thing while you make it there”.
  4. Do you ever use Serial.println in Arduino? It can really help you make sure the code is doing what you expect.

Take a look at these thoughts, tell me what you think, and I bet they can get you on the right track. I will continue to read your post, but I wanted to get a good reply back fast since we love hearing from
people who use our stuff.

1 Like

Hi layne,
Thank you so much for the quick reply. I wasn’t expecting it. I understand my post is quite long and needs time to assess. I will definitely try the MotorAngleControl example and see if it works. I remember trying a couple motor drive test in the example folder of the Bricktronics library, thought it didn’t work out the way I wanted it to. I like your explanation of PID, but I still don’t entirely understand. I simply thought if I ran the goToPosition() function, the motor would go to the exact position I would want it to go to. I have tried a couple times and yet they don’t totally seem to work. I have used Serial.println in my Arduino, mostly to check the pulseIn. The one function from you library than seemed work well was getPosition(). It ran successfully and I was able to print it in the Serial. Once again, thanks for the quick reply. I will surely run a few more tests and keep you update to date on this forum. (you can ignore the form I sent you. it has the exact same message).

Hi Layne,
I tested out the MotorSingle Program and found that it work correctly for about 3 cycles then the motor beeps and skips the setFixedDrive(75); and instead goes straight to setFixedDrive(255). To give you and example, it goes 75 for 1 sec then 255 for 1 sec the switches direction and goes -75 for 1 sec then -255 for 1 sec. But sometimes it goes 255 for 1 sec then beeps then goes -255 for another sec. I cannot afford these flaws in my project since it could mess up the driving. Also, are the Lego motors precise within speed? I need this because the input of numbers will be 100, 103, that precise. Also one last thing, I have tried analogwrite(PWM_pin, 255); and Enable and DIR pin both put on high. I was hoping that would make the motor run at speed 255. I hope you can help. Thanks!

Hi Layne,
I also ran the MotorAngleControl program. I read through most of it and sort of understand how it works. The problem is the motor doesn’t seem to reach the exact angle/position and has to move around a lot to get exactly there. Is there another way around?

Hi Layne,
I ran the MotorPositionControl program and it ran as it was supposed to. I realized that the motor is trying to stop on the exact position that was given and will correct itself in the process. The problem with using this function is that it will not work for my needs. I need the motor to stop on the dime and not continue to move, but rather go to the exact position and stop there. Hopefully, you have something in mind. Thanks a lot.