Problem using a variable to switch off DC motor, using H-Bridge

Hi there, I am new to Arduino and I am hoping someone could help me.

My aim;

To receive height measurement data from rs232, convert into TTL to be displayed on the serial monitor, then put that measurement data into a variable, using this variable to detect when minimum & maximum height values are reached and then have a delay for 2 minutes at each end.

The product has a DC motor which drives a lever up and down, the lever height is measured & has a min and max between 24.885 ---> 25.225. These are the values where I want to turn off the DC motor.

Kit I am using;

Arduino Mega2560
L298N H-Bridge
RS232 to TTL converter

How far I have got;

I have managed to read the RS232 measurement data in the serial monitor and assign it to a variable.
& I can drive the motor up & down at different speeds and directions fine!

Problem I'm having;

When my code runs, the motor fails to stop at the specified heights (or doesn't drive at all). I need the motor to stop at value 25.225, delay for 2 minutes then change direction to minimum setting and stop and delay for two minutes & loop continually.

I'm 99% certain its my bad coding, any help would be greatly appreciated!

Thanks for your time!

PS: I'm using software serial for now as the project will move onto a Uno when purchased

#include <SoftwareSerial.h>
char sylvacRad;
int enA = 9;
int in1 = 8;
int in2 = 7;

SoftwareSerial mySerial(10, 11    ); // RX, TX

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

}

void loop()
{
  while (mySerial.available()) {

    Serial.write(mySerial.read());
    delayMicroseconds(400);
    sylvacRad = mySerial.read();
    //delayMicroseconds(200);
    Serial.print(sylvacRad);
    delayMicroseconds(200);
  }

  if (sylvacRad < 25.225) {
    digitalWrite(in1, HIGH);
    digitalWrite(in2, LOW);
    analogWrite(enA, 255);
  }
  else if (sylvacRad >= 25.225) {
    digitalWrite(in1, LOW);
    digitalWrite(in2, LOW);
    analogWrite(enA, 0);
  }
}

if (sylvacRad < 25.225) sylvacRad is a char variable; it can't represent floating point values.

Thanks for your fast response! I am a beginner! Definitely a float! However....

When the variable is a char, I get a serial monitor measurement output "? 25.2211" apart from the question mark, this looks like my measurement.

When the variable is set as a float, I get "16:04:35.609 -> -1.00 32.00253.00.50.00249.00113.00" in the serial monitor.

and the code doesn't drive the motor :frowning: please advise if possible, thanks!

Definitely a float!

Nope. Definitely a char, ie a single character. But if you receive several single characters one after the other and and print them as they arrive they will look like a float but sylvacRad will still only contain a single character

You are potentially performing 2 mySerial.read() when only 1 byte may be available. Indeed, chances are very likely that you are. What is the purpose of the Serial.write(mySerial.read())?

  while (mySerial.available()) {

    Serial.write(mySerial.read());
    delayMicroseconds(400);
    sylvacRad = mySerial.read();
    //delayMicroseconds(200);
    Serial.print(sylvacRad);
    delayMicroseconds(200);
  }

I would write a test sketch that does nothing but read your software serial input and convert it to a float. Once that is working then add the motor control. You have to read in the entire ASCII string and then covert it to a float.

Here is a tutorial that will get you started:

Serial Input Basics

Thank you everyone! I will continue the project tomorrow and post again with my progress! :slight_smile:

To read an input and convert it to a float see my (draft) tutorial on Arduino Software Solutions It has a variety of solutions for reading text from Serial and converting text to int/float with pros and cons for each solution. Choose the one that suits your needs.
For your delay problem check out my tutorial on How to write Timers and Delays in Arduino
My tutorial on Multi-tasking in Arduino is worth a read also now that you are doing more then just one thing

Things are better now, String is displaying correct value except with two spaces before the first number, but the float variable is still saying 0.00.
I am assuming this is because my string has too many spaces at the beginning. Please help!

// Reads chars into a String until newline '\n'

#include <Metro.h>

int enA = 9;
int in1 = 8;
int in2 = 7;
String sylvacRad;
float FloatRad = sylvacRad.toFloat();


void setup() {
  Serial.begin(9600);
  for (int i = 10; i > 0; i--) {
    Serial.print(' '); Serial.print(i);
    delay(500);
  }
  Serial.println();
  Serial.println(F("readStringUntilLimitedTimeout.ino"));
  sylvacRad.reserve(20); // expected line size, CR + LF sylvac
}

// read Serial until until_c char found or limit char read or timeout, returns true when found/limited else false
// non-blocking, until_c, if found, is returned as last char in String, updates input String with chars read
bool readStringUntil(String& sylvacRad, char until_c, size_t char_limit) { // call with char_limit == 0 for no limit
  static bool timerRunning; static unsigned long timerStart;     // timeout static variables
  static const unsigned long timeout_mS = 1000; // 1sec  set to 0 for no timeout

  while (Serial.available()) {
    timerRunning = false; // set true below if don't return first
    char c = Serial.read();
    sylvacRad += c;
    if (c == until_c) {
      return true;
    }
    if (char_limit && (sylvacRad.length() >= char_limit)) {
      return true;
    }
    // restart timer running
    if (timeout_mS > 0) {  // only start if we have a non-zero timeout
      timerRunning = true;
      timerStart = millis();
    }
  }
  if (timerRunning && ((millis() - timerStart) > timeout_mS)) {
    timerRunning = false;
    return true;
  }
  return false;
}

char terminatingChar = '\n';

void loop() {
  ReadSerial();   //Serial to TTL to Char to String
  DriveMotor();   //loop conditions for F type cartridge
}

void ReadSerial() {
  if (readStringUntil(sylvacRad, terminatingChar, 20)) { // read until find newline or have read 20 chars, use 0 for unlimited no chars
    if (sylvacRad.lastIndexOf(terminatingChar) >= 0) {   // could also use check  if (input[input.length()-1] == terminatingChar) {
      Serial.print(sylvacRad); Serial.print(FloatRad);
    } else {
      Serial.print(F(" reached limit or timeout without newline '")); Serial.print(sylvacRad); Serial.println("'"); Serial.print(FloatRad);
    }
    sylvacRad = ""; // clear after processing for next line
  }
}
void DriveMotor() {
  while (FloatRad < 25.225); {  //while sylvacRad is not at its maximum setting, drive up
    digitalWrite(in1, HIGH);
    digitalWrite(in2, LOW);
    analogWrite(enA, 255);
  }                 //when FloatRad reaches its condition, jump onto next line
  {
    digitalWrite(in1, LOW);
    digitalWrite(in2, LOW);
    analogWrite(enA, 0);       //turn motor off to allow autoIT to do its magic
    delay (3000);     //delay for maximum amount of time to reach top + to change gain & offset
  }
  {
    while (FloatRad > 24.885) { //while sylvacRad is not at its minimum, drive down
      digitalWrite(in1, LOW);
      digitalWrite(in2, HIGH);
      analogWrite(enA, 255);
    }
    {
      digitalWrite(in1, LOW);
      digitalWrite(in2, LOW);
      analogWrite(enA, 0);
    }
    delay (3000);
  }
}

You need to do this once you have read the string:

FloatRad = sylvacRad.toFloat();

I know you did it at the beginning of the program, but it's not automatic, you need to do it explicitly.

Thanks Wild Bill, I may have prematurely post, managed to get my FloatRad to accept the values by removing the character spaces in the string, and explicitly adding the toFloat() line of code to the correct location.

It is now only giving me two decimal places when I need 4.

And I am looking for a repeat reading, and it seems to just give me a reading just once?

I will be researching a lot to find the solution but if anyone can save me time with your wisdom, I would be grateful!

// Reads chars into a String until newline '\n'

#include <Metro.h>

int enA = 9;
int in1 = 8;
int in2 = 7;
String sylvacRad;
float FloatRad;


void setup() {
  Serial.begin(9600);
  for (int i = 10; i > 0; i--) {
    Serial.print(' '); Serial.print(i);
    delay(500);
  }
  Serial.println();
  Serial.println(F("readStringUntilLimitedTimeout.ino"));
  sylvacRad.reserve(20); // expected line size, CR + LF sylvac
}

// read Serial until until_c char found or limit char read or timeout, returns true when found/limited else false
// non-blocking, until_c, if found, is returned as last char in String, updates input String with chars read
bool readStringUntil(String& sylvacRad, char until_c, size_t char_limit) { // call with char_limit == 0 for no limit
  static bool timerRunning; static unsigned long timerStart;     // timeout static variables
  static const unsigned long timeout_mS = 1000; // 1sec  set to 0 for no timeout
  while (Serial.available()) {
    timerRunning = false; // set true below if don't return first
    char c = Serial.read();
    sylvacRad += c;
    if (c == until_c) {
      return true;
    }
    if (char_limit && (sylvacRad.length() >= char_limit)) {
      return true;
    }
    // restart timer running
    if (timeout_mS > 0) {  // only start if we have a non-zero timeout
      timerRunning = true;
      timerStart = millis();
    }
  }
  if (timerRunning && ((millis() - timerStart) > timeout_mS)) {
    timerRunning = false;
    return true;
  }
  return false;
}

char terminatingChar = '\n';

void loop() {
  ReadSerial();   //Serial to TTL to Char to String
  DriveMotor();   //loop conditions for F type cartridge
}

void ReadSerial() {
  if (readStringUntil(sylvacRad, terminatingChar, 20)) { // read until find newline or have read 20 chars, use 0 for unlimited no chars
    if (sylvacRad.lastIndexOf(terminatingChar) >= 0) {   // could also use check  if (input[input.length()-1] == terminatingChar)
      sylvacRad.remove(0, 2);
      float FloatRad = sylvacRad.toFloat();
      {
    }
      Serial.print(sylvacRad); Serial.print(FloatRad);
    } else {
      Serial.print(F(" reached limit or timeout without newline '")); Serial.print(sylvacRad); Serial.println("'"); Serial.print(FloatRad);
    }
    sylvacRad = ""; // clear after processing for next line
  }
}
void DriveMotor() {
  while (FloatRad < 25.225); {  //while sylvacRad is not at its maximum setting, drive up
    digitalWrite(in1, HIGH);
    digitalWrite(in2, LOW);
    analogWrite(enA, 255);
  }                 //when FloatRad reaches its condition, jump onto next line
  {
    digitalWrite(in1, LOW);
    digitalWrite(in2, LOW);
    analogWrite(enA, 0);       //turn motor off to allow autoIT to do its magic
    delay (3000);     //delay for maximum amount of time to reach top + to change gain & offset
  }
  {
    while (FloatRad > 24.885) { //while sylvacRad is not at its minimum, drive down
      digitalWrite(in1, LOW);
      digitalWrite(in2, HIGH);
      analogWrite(enA, 255);
    }
    {
      digitalWrite(in1, LOW);
      digitalWrite(in2, LOW);
      analogWrite(enA, 0);
    }
    delay (3000);
  }
}

Serial.print defaults to two decimal places. Add ,4 to get four.

Thanks Wildbill that is all sorted now!

Now the only things that need to be done to complete this is.....

Have the serial monitor display repeated measurements, the rs232 device sends this information, maybe a couple of times a second.

With the original example from drmpf, it did this fine. Until I added the motor stuff into it , using voids and calling them in the loop and now it seems to just want to print the measurement in the serial monitor once. I'm guessing this is why the motor also isn't driving.

I think this is because I need to implement some multi-tasking with millis(), I will be working hard to figure this out.......but if anyone knows and wants to save me some time :wink:

Please post your code as it is now

Here is my code!

// Reads chars into a String until newline '\n'

int enA = 9;
int in1 = 8;
int in2 = 7;
String sylvacRad;
float FloatRad;


void setup() {
  Serial.begin(9600);
  for (int i = 10; i > 0; i--) {
    Serial.print(' '); Serial.print(i);
    delay(500);
  }
  Serial.println();
  Serial.println(F("readStringUntilLimitedTimeout.ino"));
  sylvacRad.reserve(20); // expected line size, CR + LF sylvac
}

// read Serial until until_c char found or limit char read or timeout, returns true when found/limited else false
// non-blocking, until_c, if found, is returned as last char in String, updates input String with chars read
bool readStringUntil(String& sylvacRad, char until_c, size_t char_limit) { // call with char_limit == 0 for no limit
  static bool timerRunning; static unsigned long timerStart;     // timeout static variables
  static const unsigned long timeout_mS = 1000; // 1sec  set to 0 for no timeout
  while (Serial.available()) {
    timerRunning = false; // set true below if don't return first
    char c = Serial.read();
    sylvacRad += c;
    if (c == until_c) {
      return true;
    }
    if (char_limit && (sylvacRad.length() >= char_limit)) {
      return true;
    }
    // restart timer running
    if (timeout_mS > 0) {  // only start if we have a non-zero timeout
      timerRunning = true;
      timerStart = millis();
    }
  }
  if (timerRunning && ((millis() - timerStart) > timeout_mS)) {
    timerRunning = false;
    return true;
  }
  return false;
}

char terminatingChar = '\n';

void loop() {
  ReadSerial();   //Serial to TTL to Char to String
  DriveMotor();   //loop conditions for F type cartridge
}

void ReadSerial() {
  if (readStringUntil(sylvacRad, terminatingChar, 20)) { // read until find newline or have read 20 chars, use 0 for unlimited no chars
    if (sylvacRad.lastIndexOf(terminatingChar) >= 0) {   // could also use check  if (input[input.length()-1] == terminatingChar)
      sylvacRad.remove(0, 2);
      float FloatRad = sylvacRad.toFloat();
      {
    }
      Serial.print(sylvacRad); Serial.print(FloatRad, 4);
    } else {
      Serial.print(F(" reached limit or timeout without newline '")); Serial.print(sylvacRad); Serial.println("'"); Serial.print(FloatRad);
    }
    sylvacRad = ""; // clear after processing for next line
  }
}
void DriveMotor() {
  while (FloatRad < 25.225); {  //while sylvacRad is not at its maximum setting, drive up
    digitalWrite(in1, HIGH);
    digitalWrite(in2, LOW);
    analogWrite(enA, 255);
  }                 //when FloatRad reaches its condition, jump onto next line
  {
    digitalWrite(in1, LOW);
    digitalWrite(in2, LOW);
    analogWrite(enA, 0);       //turn motor off to allow autoIT to do its magic
    delay (3000);     //delay for maximum amount of time to reach top + to change gain & offset
  }
  {
    while (FloatRad > 24.885) { //while sylvacRad is not at its minimum, drive down
      digitalWrite(in1, LOW);
      digitalWrite(in2, HIGH);
      analogWrite(enA, 255);
    }
    {
      digitalWrite(in1, LOW);
      digitalWrite(in2, LOW);
      analogWrite(enA, 0);
    }
    delay (3000);
  }
}
 while (FloatRad < 25.225);

How is FloatRad ever going to change once in this while loop ?

Same question even if you remove the semicolon, just that there is more code in the while loop

Not actually a problem but note my questions in the comments

    { //  wny is this here ?
      digitalWrite(in1, LOW);
      digitalWrite(in2, LOW);
      analogWrite(enA, 0);
    } //  and this ?
    if (sylvacRad.lastIndexOf(terminatingChar) >= 0) {   // could also use check  if (input[input.length()-1] == terminatingChar)
      sylvacRad.remove(0, 2);
      float FloatRad = sylvacRad.toFloat();
      {
    }
svlacRad.trim();
svlacRad.toFloat();

does the job

Also Arduino String toFloat() is not as robust as I like. For example 5.3.6 returns 5.3. and 5b returns 5 and abc returns 0
But may be fine for your usage.
If you want a more precise conversion checkout the SafeString conversions under Number Conversions at the bottom of
Arduino Software Solutions

  cSFP(sfStr, (char*)numStr.c_str()); // wrap String's char[] in a SafeString
  float numResult = 0;
  if (sfStr.toFloat(numResult)) { // ignores leading and trailing whitespace
    Serial.print(F(" numResult = ")); Serial.println(numResult);
  } else {
    Serial.print(F("Not a valid float '")); Serial.print(numStr); Serial.println("'");
  }

UKHeliBob:

 while (FloatRad < 25.225);

How is FloatRad ever going to change once in this while loop ?

I have a measuring device sending serial messages repeatedly about twice a second with rs232 this is then converted with a RS232 to ttl converter, the chars received are then put into a string & then converted to a float to make it compatible with my while loop. My thoughts were FloatRad's values would change as the measuring device sends new values, when the measuring device reaches this condition it would jump out the loop to turn off the motor. Plus think I need to work on my syntax :sweat_smile:

Same question even if you remove the semicolon, just that there is more code in the while loop

Not actually a problem but note my questions in the comments

    { //  wny is this here ?

digitalWrite(in1, LOW);
     digitalWrite(in2, LOW);
     analogWrite(enA, 0);
   } //  and this ?

Hi UKHelibob thanks for taking a look. This looks like it could be a classic noob syntax error, I've given it an edit, hopefully it has improved.

// Reads chars into a String until newline '\n'

//#include <Metro.h>

int enA = 9;
int in1 = 8;
int in2 = 7;
String sylvacRad;
float FloatRad;


void setup() {
  Serial.begin(9600);
  for (int i = 10; i > 0; i--) {
    Serial.print(' '); Serial.print(i);
    delay(500);
  }
  Serial.println();
  Serial.println(F("readStringUntilLimitedTimeout.ino"));
  sylvacRad.reserve(20); // expected line size, CR + LF sylvac
}

// read Serial until until_c char found or limit char read or timeout, returns true when found/limited else false
// non-blocking, until_c, if found, is returned as last char in String, updates input String with chars read
bool readStringUntil(String& sylvacRad, char until_c, size_t char_limit) { // call with char_limit == 0 for no limit
  static bool timerRunning; static unsigned long timerStart;     // timeout static variables
  static const unsigned long timeout_mS = 1000; // 1sec  set to 0 for no timeout
  while (Serial.available()) {
    timerRunning = false; // set true below if don't return first
    char c = Serial.read();
    sylvacRad += c;
    if (c == until_c) {
      return true;
    }
    if (char_limit && (sylvacRad.length() >= char_limit)) {
      return true;
    }
    // restart timer running
    if (timeout_mS > 0) {  // only start if we have a non-zero timeout
      timerRunning = true;
      timerStart = millis();
    }
  }
  if (timerRunning && ((millis() - timerStart) > timeout_mS)) {
    timerRunning = false;
    return true;
  }
  return false;
}

char terminatingChar = '\n';

void loop() {
  ReadSerial();   //Serial to TTL to Char to String
  DriveMotor();   //loop conditions for F type cartridge
}

void ReadSerial() {
  if (readStringUntil(sylvacRad, terminatingChar, 20)) { // read until find newline or have read 20 chars, use 0 for unlimited no chars
    if (sylvacRad.lastIndexOf(terminatingChar) >= 0) {   // could also use check  if (input[input.length()-1] == terminatingChar)
      sylvacRad.remove(0, 2);
      float FloatRad = sylvacRad.toFloat();
      //Serial.print(sylvacRad);
      Serial.print(FloatRad, 4);
    } else {
      Serial.print(F(" reached limit or timeout without newline '")); Serial.print(sylvacRad); Serial.println("'"); Serial.print(FloatRad);
    }
    sylvacRad = ""; // clear after processing for next line
  }
}
void DriveMotor() {
  while (FloatRad < 25.225) {  //while sylvacRad is not at its maximum setting, drive up
    digitalWrite(in1, HIGH);
    digitalWrite(in2, LOW);
    analogWrite(enA, 255);
  }                 //when FloatRad reaches its condition, jump onto next line

  digitalWrite(in1, LOW);
  digitalWrite(in2, LOW);
  analogWrite(enA, 0);       //turn motor off to allow autoIT to do its magic
  delay (3000);     //delay for maximum amount of time to reach top + to change gain & offset


  while (FloatRad > 24.885) { //while sylvacRad is not at its minimum, drive down
    digitalWrite(in1, LOW);
    digitalWrite(in2, HIGH);
    analogWrite(enA, 255);
  }

  digitalWrite(in1, LOW);
  digitalWrite(in2, LOW);
  analogWrite(enA, 0);

  delay (3000);
}

Problem is I'm still only getting a single reading, see photos labelled singlereading and repeatedresult. Because I am only getting a single reading I'm thinking this is why FloatRad won't change.

I thought at least the motor would run though, just the variable wouldn't change so it would run forever. Is this because I need to implement some multi-tasking? Thanks again

drmpf:

svlacRad.trim();

svlacRad.toFloat();



does the job

Thanks drmpf!
Does sylvacRad.trim(); work in a similar way to remove? would I enter the same (0, 2)?
And sylvacRad.toFloat(); would this make sylvacRad into a float at this instance? or would I need to declare a float called sylvacRad?

 while (FloatRad < 25.225)    //while sylvacRad is not at its maximum setting, drive up
  {
    digitalWrite(in1, HIGH);
    digitalWrite(in2, LOW);
    analogWrite(enA, 255);
  }                 //when FloatRad reaches its condition, jump onto next line

You have got rid of the endless loop in one place by removing the trailing semicolon but now it has moved on to another place as I mentioned previously. FloatRad is still not updated in the while loop so once the code enters the loop it will never exit and there is a second while loop with the same problem

Change the while to an if and the loop() function will keep running, ReadSerial() will be run.

However, in ReadSerial() you decalare a new and different variable named FloatRad that will go out of scope, ie be unavailable, outside the the if statement code block in which it is declared. It will not even be available in the else clause of that if/else

Remove the float from

      float FloatRad = sylvacRad.toFloat();

and the value will be assigned to the global FloatRad variable and that in turn will be accessible in the DriveMotor() function

As you are discovering, computer programs do exactly what you tell them to and everything needs to be laid out simply for them

1 Like