Arduino/Labview PWM issue (jerky)

Hi everyone !

I am trying to control a DC motor with an Arduino nano.
The PWM command comes from labview, is transferred by bluetooth to the Arduino nano and sent to the motor thanks to analogWrite.
I need to get datas from a sensor to use a PID controller. The data from the sensor are collected thanks to an interruption (a simple counter), processed by the arduino and sent to Labview by bluetooth. It’s also easy to do and it is working.
It looks easy to do and everything is ALMOST working BUT

when I try to control the motor, the PWM output from Arduino is jerky. I can’t find the source of this problem.

Here are two little videos of the problem, the arduino code and I join the VI.
1 : View of the motors that are not working properly. You can hear the sound. like bipbipbipbip PWM issue - YouTube
2 : View of the oscilloscope. It the PWM output from pin 6 of arduino nano
Pwm issue 2 - YouTube

volatile byte counter;
 int rpm= 0;
 int defaultVal = -1;   //will be used when labview is waiting for something and Arduino hasn't got anything to send
 long timeold;          //time value
 int interruptPin = 0, pinPWM = 6;
 String command;
 
 void setup()
 {
   pinMode ( interruptPin, INPUT );
   pinMode ( pinPWM, OUTPUT );
   counter = 0;
   Serial.setTimeout( 70 ); //to avoid waiting 1000ms between each change of the pwm value on labview
   Serial.begin ( 9600 );
   Serial.println ( "let's go" );
   attachInterrupt ( interruptPin, interruptCounter, FALLING );
   timeold = millis();
 }
 void loop()
 {
  
    if( counter >= 18 ){                  //2spins because half of the reflective parts are hidden
      detachInterrupt ( interruptPin );
      //analogWrite ( pinPWM, command.toInt() );
      /******************* TREATMENT OF THE ROTATION SPEED *******************/
                //we know that the platform made 2 spins sins we registered the time. We mow have to check the time, sustract timeold with it and we kmow how much time it took. And convert rpmilliseconds to rpminutes
      rpm = 30000 / ( millis() - timeold );
      
      /******************* GETTING READY FOR ANOTHER ROUND ******************/
      Serial.print( rpm );
      counter = 0;
      timeold = millis();   
      attachInterrupt ( interruptPin, interruptCounter, FALLING );
    }else if (millis() - timeold >= 1001){
      Serial.print( 0 );
    }
    else {
      Serial.print( defaultVal );
    }                          //Not important, just because labview is waiting to read something 

    /********************** ACTUALISING THE PWM **********************/
    command = Serial.readString();  
    analogWrite ( pinPWM, command.toInt() );
    
 }
 void interruptCounter() { ++counter;  }

Thanks for reading :smileyhappy:
If you have any idea, please tell me.

Quentin

How much time does it take for the sensor data to reach Labview and for Labview to send the PID data to the Arduino? Is the time interval constant or does it very because of the PC operating system or other factors? Seems like the PID would be more responsive if the PID were running on the Arduino.

Please read the "how to use this forum-please read" stickies to see how to properly post code.

Why are you detaching the interrupt?

Why are you bothering to calculate RPM - that is just a human concept. The computer(s) will be quite happy working with microsecs per revolution - probably happier.

And I agree with the suggestion to do the PID calcs on the Arduino.

...R

Hi ! Thank you for your answers :slight_smile:

I modified my post to display the code properly

  1. I don’t know how much time it takes for the data to tranfer… How can I measure it ?

  2. Arduino is counting the interrupts and deducing the speed. As I deduce the speed, converting in rev per min or rev per ms doesn’t change anything. So you think I should calculate the speed in ms per revolution and convert it in rpm with labview ?

  3. I will do the PID on labview (it’s already done actually, but I don’t use it as I want to solve this problem before) because it was easy and I didn’t know that it would be better doing it with arduino. But now you told me, I think I understand. I can get the speed of the platform on arduino, so doing the PID directly would avoid to wait for the data to transfer from arduino → LV and LV->arduino, right ?

  4. I saw on the internet an example code where they detached the interrupt. The results lloks the same woth or without it. Do you think I should change it ?

Thanks,

Quentin

Quentin91:
So you think I should calculate the speed in ms per revolution and convert it in rpm with labview ?

I don't think there is any need to convert it to RPM anywhere - unless you want to display something for a human to see. But that has nothing to do with speed control.

But now you told me, I think I understand. I can get the speed of the platform on arduino, so doing the PID directly would avoid to wait for the data to transfer from arduino -> LV and LV->arduino, right ?

Yes

I saw on the internet an example code where they detached the interrupt. The results lloks the same woth or without it.

Don't use code if you don't understand it. And if you don't understand something do some study to figure it out before using it.

...R

thank you.

For the development, I need to convert to rpm because we have to know what the platform can do, and I still need to improve the sensor so I need to see what it is detecting in "real time"

I understand the code I use and I wrote it myself. Disconnecting the interruption was just an advice I saw online and why not. Anyway it doesn't change, I tried with and without it.

My problem is that the PWM command is jerky and I can't figure out why. It looks like it is set to 0 at each iteration of the main loop, but why ? I want the Arduino to keep applying the same value all the time until it receives a different command...

I tried this simple code that shows the problem.

String pwm;


void setup() {
  pinMode(6, OUTPUT);
  Serial.begin(9600);
  Serial.println("go");
}

void loop() {
  pwm = Serial.readString();
  Serial.println(pwm);
  analogWrite ( 6, pwm.toInt() );
}

i write the value in the Serial Manager, the motors are turning for 2 seconds and then they stop until I apply myself a new value. How can I force the arduino to keep aplying the value all the time ?

Post the .vi so I can have a look at it

Serial.readString() can be slow because it must wait for the timeout before it completes.

It is not a good idea to use the String (capital S) class on an Arduino as it can cause memory corruption in the small memory on an Arduino. Just use cstrings - char arrays terminated with '\0' (NULL).

Have a look at the examples in Serial Input Basics - simple reliable ways to receive data. There is also a parse example to illustrate how to extract numbers from the received text.

Another thought ...
Is it possible that your Labview program is opening and closing the serial port for every message. The Arduino will reset (and forget everything) when the serial port is opened. You should make sure that the serial port is only opened once and then kept open.

...R

tsakitsan:
Post the .vi so I can have a look at it

The vi extension is not accepted on this site so here is a link to access it from google drive

Robin2:
Is it possible that your Labview program is opening and closing the serial port for every message. The Arduino will reset (and forget everything) when the serial port is opened. You should make sure that the serial port is only opened once and then kept open.

Maybe, but the problem is there without any use of labview. i tried the program I posted in py precedent message just with the a USB connection and the serial manager of Arduino IDE.

you are right about the timeout. The motors are running only before Timeout is reached and then they stops. I will try to use the cstrings and post updates later or on monday.

It is strange because the platform was spinning well two weeks ago... I tried with another arduino and it's definitly a software issue...

Thank you very much for your help.

The 1 at a time subvi is missing.

Hi everyone !

I need to control a DC motor using an arduino nano. For that I read the PWM value from a Serial connection and send it to the motor using analogWrite(). The problem is that the motor is working until the end of the timeout. Then, the value is read once again and reinitialized.

So my motor is jerky. It works for 1 second (the default value), then stops and starts again…
Here is a simple test code to show the problem.

String PWM="0";

void setup() {
  Serial.begin(9600);
  pinMode(6, OUTPUT);   // sets the pin as output
  Serial.setTimeout( 3000 );
}

void loop() {
  PWM=Serial.readString();
  analogWrite(6,PWM.toInt());
}

For this code, I need to use the serial manager, send a value. Then, the motors are running 3 seconds and stop waiting for me to send another value.

This is the code I really want to use. The PWM command is coming from LabVIEW thanks to a Bluetooth connection.

 volatile byte counter;
 int rpm = 0;
 int defaultVal = -1;   //will be used when labview is waiting for something and Arduino hasn't got anything to send
 long timeold;          //time value
 int interruptPin = 0, pinPWM = 6;
 String command;
 
 void setup()
 {
   pinMode ( interruptPin, INPUT );
   pinMode ( pinPWM, OUTPUT );
   
   counter = 0;
   rpm = 0;
   Serial.setTimeout( 70 ); //to avoid waiting 1000ms between each change of the pwm value on labview
   Serial.begin ( 9600 );
   
   Serial.println ( "let's go" );
   attachInterrupt ( interruptPin, interruptCounter, FALLING );
   timeold = millis();
 }

 void loop()
 {
  
    if( counter >= 18 ){                  //2spins because half of the reflective parts are hidden
      //detachInterrupt ( interruptPin );
      //analogWrite ( pinPWM, command.toInt() );
      /******************* TREATMENT OF THE ROTATION SPEED *******************/
                //we know that the platform made 2 spins sins we registered the time. We mow have to check the time, sustract timeold with it and we kmow how much time it took. And convert rpmilliseconds to rpminutes
      rpm = 30000 / ( millis() - timeold );
      
      /******************* GETTING READY FOR ANOTHER ROUND ******************/
      Serial.print( rpm );
      counter = 0;
      timeold = millis();   
      //attachInterrupt ( interruptPin, interruptCounter, FALLING );
    }else if (millis() - timeold >= 1001){
      Serial.print( 0 );
    }
    else {
      Serial.print( defaultVal );
    }                          //Not important, just because labview is waiting to read something 
    
    
    
    
    
    /********************** ACTUALISING THE PWM **********************/
    command = Serial.readString();  
    analogWrite ( pinPWM, command.toInt() );
    
 }

 void interruptCounter()
 {
   ++counter;
   //analogWrite ( pinPWM, command.toInt() );
 }

Here, the motors are jerky as I send the same value continuously.They run. Timout. They stops. Arduino read the value. Send it to the motors. they run. Timout…

How can i fix æy problem ? Somebody told me to use cstrings instead of String. But how can I read the value and store it to a char str[3] ?
I tried with this code but it’s doing everything but what I want…

#include <string.h>
int ledPin = 6;      // LED connected to digital pin 9
char str[3] = "000";         // variable to store the read strue
int nbr;
char letter;

void setup()
{
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);   // sets the pin as output
  Serial.setTimeout( 3000 );
}

void loop()
{
  for(int i=0;i<3;++i){
    str[i] = Serial.read();// read the input pin
    //Serial.println(str[i]);
  }
  
  nbr = atoi(str);
  Serial.println(nbr);
  analogWrite(ledPin, nbr);  // analogRead strues go from 0 to 1023, analogWrite strues from 0 to 255
}

Any advice or solution ?
Thanks for reading :slight_smile:

Quentin

Have a look at Robin2's excellent updated Serial Input Basics. Wayyyyyyy better then messing with blocking crap functions .readString() and the problematic String-class...

This seems to be a continuation of this thread.

Yes, sorry. Here is 1-at-a-time
https://drive.google.com/drive/folders/1u11t6SMELJfai_uhdN4S8iRqbdQPmbtV?usp=sharing

and thanks to you, it is working :smiley: !
The problem was effectively coming from the String class.
I followed your advised and the examples and here is the latest version of the code. I still have to change the labview program but there aren’t any problem with the PWM.

Thank you all !

const byte numChars = 4;  //number of chars we want to readchar receivedChars[numChars];
const int pinPWM = 6;
int PWMVal = 0;

boolean newData = false;

void setup() {
  pinMode(pinPWM, OUTPUT);
  Serial.begin(9600);
  Serial.println("<Arduino is ready>");
  
}

void loop(){
  reception();
  PWMVal = atoi(receivedChars);
  pwmControl(PWMVal, pinPWM);
}

void reception(){
  static boolean receptionInProgress = false;
  static byte charCount = 0;
  char endMarker = 'p';
  char rc;

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

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


void pwmControl(int pwm, int pin){
  if(newData == true){
    Serial.println(receivedChars);
    newData = false;
    analogWrite(pin, pwm);
  }
  
}

Thanks a lot guys ! It works now (kind of… but this problem is solved)

I post the code here, maybe it will help someone.

groundFungus:
This seems to be a continuation of this thread.

Yes, that’s right. I thought it would be better to make a new one for this.To me, last one was to figure out what was the problem and this one to solve it. but I missed the tutorial on the other one…

/****************** DESCRIPTION ********************
* This program is controlling the spinning platform. First, it is using the sensor to check the speed of the platform.
* Then, It gets the rpm Setpoints value from Labview. It's the speed value asked by the user
* Finally, It uses them with a PID controller to create the new PWM command for the motors
*/


/*********************************************************/
/*********************** OBJECTS *************************/
/*********************************************************/

/* ====== Objects::pwmCommand ====== */
const byte numChars = 4;  //number of chars we want to read. Here it's 3 (0,1,2,3) with the endMarker \n
char receivedChars[numChars];
const int pinPWM = 6;
int PWMVal = 0;
//static byte counter;
boolean newData = false;

/* ====== Objects::Sensor ====== */

const int pinInterrupt = 0;
int rpm = 0, countInterrupt = 0;
int defaultVal = -1;  //will be used when labview is waiting for something and Arduino hasn't got anything to send
long timer;


/* ====== Objects::PID ====== */


/*********************************************************/
/*********************** FUNCTIONS ***********************/
/*********************************************************/

/* ====== Functions::PW M====== */

void pwmControl(int pin, int pwm){
  if(newData == true){
    //Serial.println(receivedChars);
    newData = false;
    analogWrite(pin, pwm);
  }
}

void reception(){
  static byte counter = 0;
  char endMarker = 'p', rc;
  
  while(Serial.available() > 0 && newData == false){
    rc = Serial.read();
    if( rc != endMarker ){
      receivedChars[counter] = rc;
      ++counter;
      if(counter >= numChars){  //just in case
        counter = numChars - 1;
      }
    }
    else{
      receivedChars[counter] = '\0';  //if we received the endMarker, we can end the string and get ready for another round
      counter = 0;
      newData = true;
      
    }
  }
}

int convert(int val){
  if(PWMVal < 0 ) return 0;
  else if(PWMVal >255){
    return 255;
  }
  else return val;
}

/*======Functions::Sensor======*/


 void interruptCounter() { ++countInterrupt; }

 void compute(){
   if( countInterrupt >= 18 ){                  //2rotations because half of the reflective parts are hidden
      detachInterrupt ( pinInterrupt );//useless
      
                //we know that the platform made 2 spins sins we registered the time. We mow have to check the time, sustract timer with it and we kmow how much time it took. And convert rpmilliseconds to rpminutes
      rpm = 30000 / ( millis() - timer );
      
      Serial.print( rpm );
      countInterrupt = 0;
      timer = millis();   
      attachInterrupt ( pinInterrupt, interruptCounter, FALLING );
    }else if (millis() - timer >= 1001){//if the platform is not moving
      Serial.print( 0 );
    }
    else {//we have to send something to labview. He is waiting for us. We can't disappoint him else he'll timeout error
      Serial.print( defaultVal );
    }                    
  
 }
 
/************************************************************/
/************************** MAIN ****************************/
/************************************************************/

void setup() {

  /*PWM control settings*/
  pinMode(pinPWM, OUTPUT);
  
  
  /*PID settings*/

  /*General Settings*/
  Serial.begin(9600);
  Serial.println("Okay.\nLet's go !");
  Serial.setTimeout( 100 ); //to avoid waiting 1000ms between each change of the pwm value on labview
  
  /*Sensor/interrupt settings*/
  pinMode ( pinInterrupt, INPUT );
  attachInterrupt ( pinInterrupt, interruptCounter, FALLING );
  timer = millis();
}

void loop(){
  compute();
  reception();
  PWMVal = convert(atoi(receivedChars));
  pwmControl(pinPWM, PWMVal);
}

Quentin91:
Yes, that's right. I thought it would be better to make a new one for this.To me, last one was to figure out what was the problem and this one to solve it. but I missed the tutorial on the other one...

What is the point of publishing the solution separately from the problem?

Click Report to Moderator and ask to have this Thread merged with the earlier one so everyone can see all the info in one place.

...R