Go Down

Topic: Motor control with serial input (solved) (Read 102 times) previous topic - next topic

slccstudent

Sep 17, 2019, 05:51 am Last Edit: Sep 19, 2019, 05:45 am by slccstudent Reason: Mark solved
So I'm going to preface this with the obligatory *warning I'm new to arduino and programming*.

I've been trying to create a program for the arduino that will let me use serial input to control a motor and I can easily enough get it to increment or decrement by a specified quantity with a '+' and '-' key press but I've not been able to include the ability to set the motor to a specific value.

Specifically what I want to achieve is to increment and decrement with a '+' or '-' key press but also prompt for a custom value on a 't' key press.  So when a user presses 't' the prompt will read: "Input custom speed" and typing in something like say '50' will set the motor speed to 50.

Unfortunately all my program is doing currently on a 't' key press is saying "Input Custom Speed" and automatically setting it to 0.

I've read the serial input basics tutorial and although some of it still goes over my head I've started to get a grasp on it.  I'm also a complete novice to programming in general and read up on the C language for the specific use cases I've tried but am probably doing things incorrect or have things in the code that could be trimmed.

Here are the specific bits of code in my program that I can't get to quite work:

The input-
Code: [Select]

void Start_input()
{
  static byte ndx = 0;
  char endMarker = '\n';

  if (Serial.available() > 0 && secondloop == false)
  {
    do
    {
      c = Serial.read();
      newData = true;
    }
    while (newData == false);
  }

  if (Serial.available() > 0 && secondloop == true)
  {
    do
    {
      c = Serial.read();

      if (c != endMarker) {
        receivedChars[ndx] = c;
        ndx++;
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      }
      else {
        receivedChars[ndx] = '\0'; // terminate the string
        ndx = 0;
        newData = true;
      }
    }
    while (newData == false);
  }
}



The part of the code dealing with 't' press:

Code: [Select]

void motor_ctl()
{
if (c == 't' && secondloop == false)
  {
        Serial.println("Input custom speed");
        secondloop = true;
  }

  if (c == 't' && secondloop == true && newData== true)
  {
      dataNumber = atoi(receivedChars);
      pwm_speed = dataNumber;
      Serial.print("Motor speed is ");
      Serial.println(pwm_speed);
      newData = false;
      secondloop = false;
  }
analogWrite(motor, pwm_speed);
}


And here's the main body of the code with variables and such in case that can shed light on something:
Code: [Select]
int motor = 3; //define motor as pin 3
int pwm_speed = 127; //initial speed of motor
int value = 10; //value to increment or decrement motor speed by on key press

char c; //

int dataNumber; //variable with placeholder value for integer serial input

const byte numChars = 32; //Set a read-only value of 32 bytes and assign it to numChars
char receivedChars[numChars]; //define variable of type character as an array of size numChars

boolean newData = false; //variable to control interaction between my functions

boolean secondloop = false;



void setup()
{
  Motor_Setup(); //start up function for motor
}

void loop()
{
  Start_input();
  motor_ctl(); //function to control speed on key press
}


Any help is appreciated.

Deva_Rishi

You Serial reception ode looks quite a lot like Robin2's Serial input basics example though a bit mangled (probably not fully understood) anyway here's a hint
Code: [Select]
if (c == 't' && secondloop == true && newData== true)
  {
      dataNumber = atoi(receivedChars);
If c=='t' then what is the contents of receivedChars ? for atoi() to return anything other than 0 the c-string may not contain anything other than numerals.

What i mean with mangled, is that you use the data read variable c outside of the reception function, this is not such a good idea. The plan should be
- first receive all data (set the line ending in the serial monitor to something and look for that character or add a terminating character yourself)
- then parse the data, extract the information you want from it. So you may not want to prompt the user for a new speed, rather you may want the user to enter "t80" and change the tempo according to that.

finally since the main body of the program does not take a lot of time the possibility exists the buffer is empty before completely read, and therefore incomplete data may be acted upon (hence the terminating character) This depends on the BAUD rate as well, but you did not post a full sketch.
To 'Correct' you have to be Correct. (and not be condescending..)

Robin2

It is much easier to help if you post a complete program in one piece. That way we can be sure we have not missed anything critical.

If you are basing your code on Serial Input Basics then, like @Deva-Rishi, I strongly recommend that you use it without any changes - on the basis that "if it works, don't fix it"

There is a simple user-input example in Planning and Implementing a Program

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

Deva_Rishi

actually this condition can never be true
Code: [Select]
if (c == 't' && secondloop == true && newData== true)once secondloop is true, c will hold the value of the last input character, iow the last character to be added to receivedChars.
To 'Correct' you have to be Correct. (and not be condescending..)

slccstudent

Thanks for the advice!  I went ahead and took your suggestions and got it working.  Here's the completed code:

Code: [Select]
int motor = 3; //define motor as pin 3
int pwm_speed = 127; //initial speed of motor
int value = 10; //value to increment or decrement motor speed by on key press

char c; //

const byte numChars = 32; //Set a read-only value of 32 bytes and assign it to numChars
char receivedChars[numChars]; //define variable of type character as an array of size numChars

boolean newData = false; //variable to control interaction between my functions

const byte newString = 32;
char access[newString];


void setup()
{
  Motor_Setup(); //start up function for motor
  Serial.println("<Arduino is ready>");
}

void loop()
{
  Start_input();
  motor_ctl(); //function to control speed on key press
}

void Motor_Setup()
{
  pinMode(motor, OUTPUT); //initialize motor
  Serial.begin(9600); //initialize serial communication
  analogWrite(motor, pwm_speed); //start motor at set value
}

void Start_input() {
  static byte ndx = 0;
  char endMarker = '\n';
  char rc;

  while (Serial.available() > 0 && newData == false) {
    rc = Serial.read();

    if (rc != endMarker) {
      receivedChars[ndx] = rc;
      ndx++;
      if (ndx >= numChars) {
        ndx = numChars - 1;
      }
    }
    else {
      receivedChars[ndx] = '\0'; // terminate the string
      ndx = 0;
      newData = true;
    }
  }
}

void motor_ctl() //function to allow user input to control motor, display current speed,
//and tell user when max or minimum speed is reached
{
  c = receivedChars[0];
  if (c == '+' && newData == true)

  {
    if (pwm_speed <= (255 - value)) //Do this when current motor speed is less than
      //or equal to specified cut off value
    {
      pwm_speed = pwm_speed + value;
      Serial.print("Motor speed is ");
      Serial.println(pwm_speed);
    }
    else if ((pwm_speed > (255 - value)) && (pwm_speed < 255)) //Do this when motor
      //speed is above
      //cut off value
    {
      pwm_speed = 255;
      Serial.print("Motor speed is ");
      Serial.println(pwm_speed);
    }
    else //Do this when key is pressed while motor already at max speed
    {
      Serial.println("Giving her all she's got");
    }
    newData = false;
  }

  else if (c == '-' && newData == true) //On - key press
  {
    if (pwm_speed >= value) //Do this when speed is above cut off value
    {
      pwm_speed = pwm_speed - value;
      Serial.print("Motor speed is ");
      Serial.println(pwm_speed);
    }
    else if ((pwm_speed < value) && (pwm_speed > 0)) //Do this when speed is
      //below cut off value
    {
      pwm_speed = 0;
      Serial.print("Motor speed is ");
      Serial.println(pwm_speed);
    }
    else //When motor is already at lowest possible
    {
      Serial.println("Reverse speed not possible");
    }
    newData = false;
  }

  else if (c == 't' && newData == true) //On 't' input
  {
    int newSpeed;
    char *p = receivedChars + 1; //start reading string at second value
    strcpy(access, p); // copy string value from receivedChars to access starting at second value
    int input;
    input = atoi(access); //integer value from string access
   
    if (((input > 0) && (input <= 255)) || (strcmp(access, "0") == 0)) // if integer value of array is above 0 and equal to or less than 255.
    //or if the result of comparing string access to the string "0" is 0/the same.
    {
      newSpeed = input; //convert new array string to an integer value
      pwm_speed = newSpeed;
      Serial.print("Motor speed is ");
      Serial.println(pwm_speed);
      newData = false;
    }
    else
    {
      Serial.println("Invalid Input");
      newData = false;
    }
  }

else
{
  analogWrite(motor, pwm_speed); //Commit new speeds given by
  //output of above statements
  newData = false;
}
}


I'm somewhat dissatisfied that most of what I'm doing is just mimicry and that I burned through several dozens of different  trials to reach this end result despite having a clear idea of how I wanted it to behave from the start.  I also still don't fully understand why many of my results failed but I've only been working with the Arduino and C for a couple of weeks so I think it's to be expected.  Hopefully as I keep working through projects I'll be able to pick it up better and can come to rely on google and tutorials less.

anthonyHope


Go Up