Help me build an electric car!

I made a PWM program with an H-Bridge and three buttons for Start, Stop and Reverse.
AND IT WORKS (pretty much)! I have
Serial.println(analogRead(A0));
Serial.println(speed1);
Serial.println(rotDirection);

When I stop, I had to set speed1 =0, because serial kept outputting the last speed1.

When the potentiometer is closed all the way and speed1=0, pushing the start button doesn’t start the motors. They are at speed1 = 0. But when I push the Start button and open the potentiometer, the motors don’t go until I push Start again with the potentiometer sending signal into A0.

Why doesn’t it go into a ready state?
Like, the motors should be engaged but motionless, so the motors start to turn when I increase A0.

When these if statements are removed and TurnMotorA is the only function running, the motors start turning as you open the pot.

And the serial output isn’t faithful in real time. Pot is fully closed, the motor isn’t running, and A0 = 290.

#include <Arduino.h>

const int in1 = 8;

const int in2 = 9;

const int ConA = 10;

const int in3 = 7;    

const int in4 = 6;

const int ConB = 5;

const int buttonStart = 4;

const int buttonStop = 3;

const int buttonRev = 2;

int startState = 0;

int stopState = 0;

int revState = 0;

int speed1 = 0;

int rotDirection = 0;

void setup()

{Serial.begin(9600);

  pinMode(4, INPUT);

  pinMode(3, INPUT);

  pinMode(2, INPUT);

  pinMode(5, OUTPUT);

  pinMode(6, OUTPUT);

  pinMode(7, OUTPUT);

  pinMode(8, OUTPUT);

  pinMode(9, OUTPUT);

  pinMode(10, OUTPUT);

}

 

void TurnMotorA()

{

// We create a function which control the direction and speed

digitalWrite(in1, LOW); // Switch between this HIGH and LOW to change direction

digitalWrite(in2, HIGH);

digitalWrite(in3, LOW); // Switch between this HIGH and LOW to change direction

digitalWrite(in4, HIGH);

speed1 = analogRead(A0);

speed1 = map(speed1, 0, 1023, 0, 255);

analogWrite(ConA, speed1); // Then inject it to our motor

analogWrite(ConB, speed1); // Then inject it to our motor

rotDirection = 0;

}

void StopMotorA()

{

  digitalWrite(in1, LOW); // Switch between this HIGH and LOW to change direction

  digitalWrite(in2, LOW);

  digitalWrite(in3, LOW); // Switch between this HIGH and LOW to change direction

  digitalWrite(in4, LOW);

}

void RevMotorA()

{

  // We create a function which control the direction and speed

  digitalWrite(in1, HIGH); // Switch between this HIGH and LOW to change direction

  digitalWrite(in2, LOW);

  digitalWrite(in3, HIGH); // Switch between this HIGH and LOW to change direction

  digitalWrite(in4, LOW);

  speed1 = analogRead(A0);

  speed1 = map(speed1, 0, 1023, 0, 255);

  analogWrite(ConA, speed1); // Then inject it to our motor

  analogWrite(ConB, speed1); // Then inject it to our motor

  rotDirection = 1;

}

 

void loop()

{

  Serial.println(analogRead(A0));

  Serial.println(speed1);

  Serial.println(rotDirection);

  Serial.println();

stopState = digitalRead(buttonStop);

revState = digitalRead(buttonRev);

startState = digitalRead(buttonStart);

  if (stopState == HIGH)

  {

    StopMotorA();

    speed1 = 0;

    }

    if (revState == HIGH)

    {

      RevMotorA();

    }

    if (startState == HIGH)

    {

      TurnMotorA();

    }

  }

I might be well wide of the mark here Joe, it’s late and I should be asleep! However, pushbuttons bounce.

There’s a sketch on Arduino.cc to toggle an LED with a pushbutton. It works randomly because the button bounces. You press and it makes and breaks multiple times. The Arduino can read all these makes and breaks, so you end up with the LED only working if there was an odd number of makes!

You can debounce switches with a resistor and a capacitor. Also with the addition of a Schmidt Trigger, or with delays in software. You can also buy dedicated switch debouncing chips.

Are your buttons externally pulled up or down with a resistor? You have configured them as input as opposed to input_pullup. If no external resistors then your inputs are floating and don’t have a defined high or low, they have high, low, and random. Make them input_pullup, but be aware that this means that they are high when not pressed and low when pressed.

HTH

Cheers,
Norm.

You can also debounce switches and push-buttons in software - there are many examples if you Google ‘arduino debounce’).
Susan

1 Like

Thank you very much, but I’m not interested in bouncing. The physical setup is such that there is no bounce, and the loop is so fast that we don’t need to see the button at the first contact. It’s not like a rotary encoder, where we can’t make a selection without sensing the tick.

Let me describe it better. I have a potentiometer at A0 to map speed1. Speed1 is the only speed.
There’s a button at GPIO3. I went back to Arduino and looked up how to light an LED with a button, so now my buttons are all wired right.

I want to start loop with TurnMotorA and then let the buttons initiate the other funcitons (Stop, rev, turn), but that program doesn’t run.

So now when the program begins no function (Start, Stop, Rev) is running. When I push the Start button, the motors turn if the potentiometer is open. But when the pot is closed, it obviously doesn’t start. Trouble is, when I push the start button and then open the pot, it doesn’t initiate the motors. I have to push the Start button again with A0 > 0.

This is supposed to work like a car. You go forward from zero to travel speed, then you stop and back up.
How can I make it go from zero?

Hi Joe,

Here is a link to a document which is a PDF of an appendix from my currently in progress book. It covers a number of debouncing techniques for Arduino etc. Hopefully, you will find it interesting and helpful.

It contains the odd occasional reference to other chapters in the book, and figures in those other chapters. You can ignore those for the most part.

For debouncing switches I use an MC14490P hex debounce chip, or the “History” method – but there are others documented which also work fine.

Cheers,
Norm.

I’m afrai that the laws od Physics overrules you on that point Joe. There is bouncing and the microcontroller picks them all up, and your loop() function will definitely be collecting more than one switch state. Trust me on this – I’ve been down that route myself often enough.

Do you have external pull-up or pull-down resistors attached to your switches? If not, you will be unlikely to get a clean reading when the switch is not being pressed as it is floating. Floating inputs are a seriously bad thing and will conspire to bollox up your code and project.

DC motors are notorious for not liking to run at slow RPM. At least, not without a decent gearbox to gear down the motor’s RPM to whatever you desire. Do you have geared motors?

When you press the start button, the motor – obviously – will not run without being told the speed you need it to run at. Given they don’t like slow speed running, I suspect the motor has stalled when you enable it with the speed at zero. It’s hard to tell without a diagram/schematic of your setup.

So questions:

Q1. Pull-up or pull-down resistors on your buttons? Yes or no?

Q2. Are your motors geared or not?

Q3. Can you draw us a diagram please of your setup?

Statement of fact 1. You do, definitely suffer from switch bounce and your loop() will detect all or nearly all of the switch state changes.

Cheers,
Norm.

I have no interest in bouncing. Not relevant here.
The switches will be BLE, so no contact at all when I build it. This is good enough.

Not using PULLUP, copied code straight from Arduino.cc.
And the buttons initiate the functions as intended.

I just want to start from speed1=0.
When I press the Start button, I can open the pot all the way without so much as a twitch from the motor. It would appear that the function quit and the loop started again with no function running.

But I want the car to start moving from zero like in real life.

Reading the button was returning 3. Now it’s 1 or 0.

Hi Joe,

as I mentioned above, you might not be interested in bouncy switches but the microcontroller definitely is interested and they will bite you. I know this becuase Physics, plus I’ve had exactly the same bouncy switch problem with a simple “push a button to light an LED” sketch copied from the Arduino web site! Possibly the same one you are using?

|Anyway, if you get me a diagram, I can wire something up here ad try it out. Thanks.

Cheers,
Norm.

AH! Here’s the question:

How can I make it keep trying to initiate the motors while A0 input = 0?

This really is fun, so maybe you want to try it.





I think I see what might be wrong. Maybe.

in loop() you check for the state of the start button. You press that once, I assume, and bouncing excepted, you want this to start the motor. So you call out to TurnMotorA(). That feeds a speed of zero to the controller resulting in nothing. The function then returns to loop() and exits.

Next time through theloop() you are NOT pressing the start button, so nothing happens.

If you have the speed turned up to 11, from 0, then when you press start again, the motor will turn because there is a speed to run at.

The problem is that your start button state has been forgotten!

You need a variable to hold the “motorRunning” state. When the start button is pressed, set motorRunning to 1, when the stop button is pressed, set it to zero. Now in the loop, you need to check motorRunning and if not zero, you should read the speed setting and pass that to the appropriate connections.

Basically,:

  • If not motorRuning, then you are only interested in star button. Speed, reverse etc can be ignored.
  • Otherwise:
    • Scan for speed, and set the connections accordingly.
    • Check the reverse button and if pressed, change direction to the same speed.
    • Check the stop button and if pressed, set motorRunning to zero, and stop the motor(s) running.

HTH

Cheers,.
Norm.

Image 1. You are using external pull down resistors. They connect the switch input to the Arduino, to ground. Reading the switches will give a LOW if unpressed and a HIGH when pressed. This is good. Thanks.

Image 2. You have ungeared motors. They will give you problems at low RPM, they simply don;t have any torque at low RPM so no turning force.

Image 4. I don’t think you are wired correctly from A0. The wiper of the pot should go to A0 and the two ends of the pot should connect to VCC and GND. The diagram shown in the image appears to show that A0 connects to the top of the pot and 5V to the bottom with nothing on the wiper. It should be:

5V ---> XXXXXX ---> GND
           |
           +------------> A0

See my previous reply for why I think you are having the problems you are describing and what I think you should do to alleviate them. Basically, your program doesn’t know that the “ignition” is turned on whenever you pass through the loop() and the start button is not being pressed.

Cheers,
Norm.

I suggest that your code should resemble this, perhaps? It copes, hopefully, with any speed changes each time trough the loop() and should fix the non-functioning start button.

#include <Arduino.h>

const int in1 = 8;
const int in2 = 9;
const int ConA = 10;
const int in3 = 7;    
const int in4 = 6;
const int ConB = 5;
const int buttonStart = 4;
const int buttonStop = 3;
const int buttonRev = 2;

int startState = 0;
int stopState = 0;
int revState = 0;
int speed1 = 0;
int prevSpeed = 0;
int rotDirection = 0;

int motorRunning = 0;



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

  pinMode(4, INPUT);
  pinMode(3, INPUT);
  pinMode(2, INPUT);
  
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
}

 

void TurnMotorA() {
  // We create a function which control the direction and speed
  
  digitalWrite(in1, LOW); // Switch between this HIGH and LOW to change direction
  digitalWrite(in2, HIGH);
  digitalWrite(in3, LOW); // Switch between this HIGH and LOW to change direction
  digitalWrite(in4, HIGH);
  analogWrite(ConA, speed1); // Then inject it to our motor
  analogWrite(ConB, speed1); // Then inject it to our motor
  rotDirection = 0;
}



void StopMotorA() {
  digitalWrite(in1, LOW); // Switch between this HIGH and LOW to change direction
  digitalWrite(in2, LOW);
  digitalWrite(in3, LOW); // Switch between this HIGH and LOW to change direction
  digitalWrite(in4, LOW);
}



void RevMotorA() {
  // We create a function which control the direction and speed

  digitalWrite(in1, HIGH); // Switch between this HIGH and LOW to change direction
  digitalWrite(in2, LOW);
  digitalWrite(in3, HIGH); // Switch between this HIGH and LOW to change direction
  digitalWrite(in4, LOW);
  analogWrite(ConA, speed1); // Then inject it to our motor
  analogWrite(ConB, speed1); // Then inject it to our motor
  rotDirection = 1;
}



void loop() {
  Serial.println(analogRead(A0));
  Serial.println(speed1);
  Serial.println(rotDirection);
  Serial.println();


  // We should always read the speed -- it may have changed.
  speed1 = analogRead(A0);
  speed1 = map(speed1, 0, 1023, 0, 255);

  // If the motor is not running, check if it should
  // be started. If so, set the flag to say "we are running"
  // and set the speed to whatever the pot indicates.
  if (!motorRunning) {
    // Read the start button only.
    startState = digitalRead(buttonStart);

    // If pressed, turn on the ignition!
    if (startState) {
      motorRunning = 1;
      TurnMotorA();
      return; // Exit from this run of loop().
    }
  }

  
  // The motor is already running. Check the other buttons.

  // Should we stop it?
  stopState = digitalRead(buttonStop);
  if (stopState) {
    motorRunning = 0;
    StopMotorA();
    speed1 = 0;
  }

  // Or reverse it?
  revState = digitalRead(buttonRev);
  if (revState) {
    RevMotorA();
  }

  // Did the speed change? If so, adjust the RPM.
  if (speed1 != prevSpeed) {
      analogWrite(ConA, speed1);
      analogWrite(ConB, speed1);

      // And save the new speed setting.
      prevSpeed = speed1;
  }

}

Cheers,
Norm.

Ah. Right. I want to start using state in my programming, but I haven’t implemented that yet.

Thank you.

1 Like

Thanks again!
Yes, it works like you said it would. I can press the ON button, and turn the knob from zero and initiate the motor.

Question:
if (speed1 != prevSpeed) {

Speed1 is already tied to A0. So that stays updated, doesn’t it? Why if the speed?

My aim here was to simply do PWM to model the basics of an electric car. Some scripts write out duty cycle. I plan to investigate the duty cycle of the PWM pins on the Uno. Duty cycle is how Tesla gets a 5,000 lb car to go 400 miles and blast it forward at unreasonable speeds.
But I’m also trying to learn to program, so thanks for giving me a great example of state.

So now I want to add another potentiometer for steering and hook up a servo.

I don’t really like bluetooth, so I’m just going to copy a project I saw: bluetooth spycam robot.

I’m not going to actually learn to transfer control to wireless that way. ESP Now might be fun.

1 Like

Holy crap- it works! (Poorly…)

This is what I did for the servo turn:
turn = analogRead(A1);
turn = map(turn, 0, 1023, 0, 180);

But it never rests. It wobbles back and forth until I get to either end of the potentiometer.

If the speed is the same as it was on the last trip through the loop() then there’s nothing to change. So, test if the speed changed, and only if so, update the motor speeds to match. The first rule of optimisation is never do unnecessary work.

AnalogWrite() does a lot of work in the background before it gets around to actually setting the value you requested. If there’s no need to change the speed, you save all that time for another pass through the loop.

Actually, all the Arduino functions do a lot of work in the background before they do what you requested! :grin:

Anyway, glad it’s all working now.

Cheers,
Norm.

Could this 9g servo be burnt out already???

I don’t know if ‘analogRead()’ does any averaging etc but most ADCs will vary in the least significant bit or 2, even with a ‘steady’ input signal.
Therefore if can be better to test for a difference with code such as

if( abs(speed1 - prev_speed) > 4)
{…}

The value of ‘4’ may need to be played with to find the right value for the ‘steady state’ but still pick up small movements of the potentiometer.
Susan

1 Like