Having issues with BLDC motor speed fluctuating with new code involving a display

Firstly I will post the code at hand. Excuse the poorly written and copy pasted code I've moved around trying to find the issue I'm new to this....

#include <Servo.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>


Servo ESC;     // create servo object to control the ESC

int potValue;  // value from the analog pin
int convert;
int percentvalue;
int potRead;
#define OLED_RESET 4                              //Digital pin 4 set aside for OLED reset
Adafruit_SSD1306 display(OLED_RESET); 
void setup() {
  ESC.attach(9,1000,2000); // (pin, min pulse width, max pulse width in microseconds) 
  display.begin(SSD1306_SWITCHCAPVCC, 0x3c);      //start the oled called display with a a hex addy of 0x3c
  display.display();                              //Show what's in the buffer
  delay(1000);                                    //take a breath
  display.clearDisplay();                         //clear the screen
         
  // Attach the ESC on pin 9
  
  

}

void loop() {
  potValue = analogRead(A0);                         //read the voltage on pin A0 and put the ADC value into variable level
   potRead = map(potValue, 0, 1023, 0, 180);   // scale it to use it with the servo library (value between 0 and 180)
  ESC.write(potRead);    // Send the signal to the ESC
convert = map(potValue, 140, 1023, 0, 128);            //convert the 0-1024 raw ADC to a more OLED friendly 1-128
percentvalue = map(potValue, 140, 1023, 0, 100);            //convert the 0-1024 raw ADC to a more OLED friendly 1-128
 
 
  
    static const unsigned char PROGMEM image_Layer_4_bits[] = {0x70,0x00,0x88,0x20,0x88,0x40,0x88,0x80,0x71,0x00,0x02,0x00,0x04,0xe0,0x09,0x10,0x11,0x10,0x21,0x10,0x40,0xe0};
  static const unsigned char PROGMEM image_Layer_3_bits[] = {0x07,0xff,0xfc,0x08,0x00,0x02,0x08,0x00,0x02,0x78,0x00,0x02,0xf8,0x00,0x02,0xf8,0x00,0x02,0xf8,0x00,0x02,0xf8,0x00,0x02,0x78,0x00,0x02,0x08,0x00,0x02,0x08,0x00,0x02,0x07,0xff,0xfc};
  

 if (percentvalue <= 0){
  display.clearDisplay();
  display.drawBitmap(105, 0, image_Layer_3_bits, 23, 12, 1);
  display.drawBitmap(53, 2, image_Layer_4_bits, 12, 11, 1);
  display.setTextSize(2);                         //set up text size
  display.setTextColor(WHITE);                    //set text color
  display.setCursor(40,0);                        //where to position cursor (128,64)
  display.fillRect(1, 20, 0, 20, WHITE);    //Draw a rectangle (x,y,width,height,color)
  //display.clearDisplay();                       //another flush
  display.println(0);                         //add data in the buffer - the variable level
  display.display();  


 }
 else if ((percentvalue <= 9) && (percentvalue >= 0)){
  display.clearDisplay();
  display.drawBitmap(105, 0, image_Layer_3_bits, 23, 12, 1);
display.drawBitmap(53, 2, image_Layer_4_bits, 12, 11, 1);
  display.setTextSize(2);                         //set up text size
  display.setTextColor(WHITE);                    //set text color
  display.setCursor(40,0);                        //where to position cursor (128,64)
  display.fillRect(1, 20, convert, 20, WHITE);    //Draw a rectangle (x,y,width,height,color)
  //display.clearDisplay();                       //another flush
  display.println(percentvalue);                         //add data in the buffer - the variable level
  display.display();                              //show the buffer

}
else if ((percentvalue >= 10) && (percentvalue <= 99)){
  display.clearDisplay();
  display.drawBitmap(105, 0, image_Layer_3_bits, 23, 12, 1);
  display.drawBitmap(65, 2, image_Layer_4_bits, 12, 11, 1);
  display.setTextSize(2);                         //set up text size
  display.setTextColor(WHITE);                    //set text color
  display.setCursor(40,0);                        //where to position cursor (128,64)
  display.fillRect(1, 20, convert, 20, WHITE);    //Draw a rectangle (x,y,width,height,color)
  //display.clearDisplay();                       //another flush
  display.println(percentvalue);                         //add data in the buffer - the variable level
  display.display();  

}

  else if (percentvalue >= 100){
  display.clearDisplay();
  display.drawBitmap(105, 0, image_Layer_3_bits, 23, 12, 1);
  display.drawBitmap(76, 2, image_Layer_4_bits, 12, 11, 1);
  display.setTextSize(2);                         //set up text size
  display.setTextColor(WHITE);                    //set text color
  display.setCursor(40,0);                        //where to position cursor (128,64)
  display.fillRect(1, 20, convert, 20, WHITE);    //Draw a rectangle (x,y,width,height,color)
  //display.clearDisplay();                       //another flush
  display.println(percentvalue);                         //add data in the buffer - the variable level
  display.display();  
}

}


My issue is that with this code my motor will fluctuate speed just slightly, up and down.

I'm not sure if this is happening due to my mappings or if its the rate at which the code executes.

If I run just a very basic ESC code I do not get the issue. If I comment out all but the first if statement it also seems to run without these fluctuations.

Any info would be appreciated.. Its late and I have been at it for hours beating my head against the breadboard...

I would like to add that I recently found that most of the fluctuation goes away if I connect the + side of the potentiometer to Vcc instead of directly to the battery, I'm assuming with Vcc giving a more stable voltage that it isn't causing as much swing to the ADC presented to the input.

However, this still has me scratching my head because I have no issues with surging when + is connected to raw voltage and I run the very basic ESC BLDC motor code

#include <Servo.h>

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
Servo ESC;     // create servo object to control the ESC

int potValue;  // value from the analog pin
#define OLED_RESET 4                              //Digital pin 4 set aside for OLED reset
Adafruit_SSD1306 display(OLED_RESET); 

void setup() {
                               
  // Attach the ESC on pin 9
  ESC.attach(9,1000,2000); // (pin, min pulse width, max pulse width in microseconds) 
}

void loop() {
  potValue = analogRead(A0);   // reads the value of the potentiometer (value between 0 and 1023)
  potValue = map(potValue, 0, 1023, 0, 180);   // scale it to use it with the servo library (value between 0 and 180)
  ESC.write(potValue);    // Send the signal to the ESC
}

This leads me to believe that there is still a fault in my original code that is either causing processing delays or is causing more variation in the pulse width that is being sent to the ESC than I am getting with the above code

Why are you not using serial.Print() to show the actual value being read and also the value being used after scaling the reading. That would certainly either show the problem or tell you that is not where the problem is located. Right now you are just guessing and that is not a good way to debug programs.

1 Like

Using the delay() function in Arduino sketches can cause your program to halt during the delay period, preventing it from performing other tasks 'simultaneously'. To achieve non-blocking delays, you can use the millis() function, which allows your program to continue running other code while waiting. Here's how you can implement it:

millis()

The millis() function returns the number of milliseconds that have passed since the Arduino board began running the current program. By comparing the current time to a previously recorded time, you can execute code at specific intervals without stopping the entire program.

In regards to the "delay", as I understand it, is only in the setup and shouldnt harm the loop code.

As for the serial print, i will add it back in and see what data it spits back. Where exactly should I implement it? Before the if statements i assume, as my if statements only apply to my display and do not influence the motor?

To effectively debug your Arduino program and identify performance bottlenecks, you can utilize the Serial Monitor's timestamp feature alongside strategic Serial.print() statements. Here's how to proceed:

  1. Enable Timestamps in Serial Monitor:
  • In the Arduino IDE's Serial Monitor, locate the option to display timestamps. This feature is available in Arduino IDE version 1.8.8 and above. Enabling it will prepend each line of serial output with the time elapsed since the Serial Monitor was opened, aiding in performance analysis.

YouTube
2. Insert Debugging Statements:

  • After each significant code segment, add a Serial.print() statement to output a unique identifier. For example:

cpp

Copy code

Serial.print("Checkpoint 1");
  • This practice allows you to track the program's flow and measure the time taken between checkpoints.
  1. Monitor for Serial Flooding:
  • Be cautious of placing Serial.print() statements within loops that execute rapidly, as this can overwhelm the Serial Monitor, leading to a "serial flood." To mitigate this, consider implementing a delay or a condition to limit the frequency of serial outputs.
  1. Analyze the Output:
  • Run your program and observe the Serial Monitor. The timestamps will help you determine the time elapsed between checkpoints, enabling you to identify sections of code that may require optimization.

By following these steps, you can gain valuable insights into your program's execution and enhance its performance.

Thank you for this direction. I will start by doing 2 things.

I will apply serial.print to the basic BLDC code to check its feedback. I will then apply serial print to my Frankenstein code first starting only at the start of the loop, just before esc.write then after checking those values i will apply it both before esc.write and after the last Else If statement. If seeing variance I will then apply it in each If statement as well.

Seem about right?

Would too much info being sent to the buffer cause a delay in the pwm output or otherwise slow down the code execution as a whole?

This is all being done on an arduino micro by the way if of any significance.

Probably. That is why you need to add the serial.Print stuff at one location, only and test to see if that looks correct. Then delete that code and add serial.print in the next location and test. DO NOT try to do all at once!

You could use millis() to measure the time and average several samples through the loop, then print the result. This will help show any deviations.

To make it easier, you can create a function to handle the accumulation and averaging. You can simply call the function (e.g., Test()) in your code wherever you need it. Here's an example:


Example Code:

// Function to accumulate and average time
void Test() {
  static unsigned long prevTime = 0;
  static unsigned long totalTime = 0;
  static int sampleCount = 0;

  // Measure the current time
  unsigned long currentTime = millis();
  
  // If this is not the first run, calculate the time difference
  if (prevTime != 0) {
    unsigned long elapsedTime = currentTime - prevTime;
    totalTime += elapsedTime; // Accumulate the elapsed time
    sampleCount++;
  }
  
  // Update the previous time
  prevTime = currentTime;

  // Print the average time every 10 samples
  if (sampleCount == 10) {
    unsigned long avgTime = totalTime / sampleCount;
    Serial.print("Average time: ");
    Serial.println(avgTime);
    
    // Reset the counters for the next round of averaging
    totalTime = 0;
    sampleCount = 0;
  }
}

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

void loop() {
  Test(); // Call the Test function to measure and average timing
  delay(100); // Simulate some work in the loop
}

This code would be to monitor the time/ing of the code execution if I understand it correctly?

Edit: If deployed with the Frankenstein motor code I have written would give the timing(s) of its execution? Would the If statement clash with my first If statement or just create an And If situation? That still seems it would clash but would likely only slow the display readouts?

You got it.

    • The Test() function keeps track of the time difference between consecutive calls using millis().
  • It accumulates the time differences and calculates the average after every 10 samples.

How could I best apply this sampling type code to my readouts and feedback of the potentiometer?

I just ran a couple quick Serial.println() commands in various areas. The readings fluctuate approximately the same amount both in the most basic BLDC code and my Frankenstein code. So before I get into monitoring the timings I feel as though my issue may be solved with taking averages of my potentiometer or the PWM signal to smooth the fluctuation out.

While I feel that the noticable flucuation between Basic and Frankenstein code might be due to time to execute, I think I might be able to just take an average before feeding a PWM signal to keep the PWM signal smooth and therefor keep the motor speed constant.

This is not to say that I won't test the execution timing to see if that is why the PWM variance is more noticable in the FrankenCode as I will still likely do that. But in both code examples I am seeing the same approximate variance in both the Analog signal value and the PWM mapping.

So imo (again this is pretty new to me) I feel like handling an average would give a smoother signal to the ESC regardless of code execution time. Am I sounding like an idiot that just isn't listening or is my understanding and current logic sound?

Addition edit: I would like to also thank you all for the quick responses. I was worried I may be days between responses :upside_down_face:

Also another question that I can't seem to get around, I would like to be able to power the BLDC motor with a full 7v+ from my battery but at the same time have the arduino plugged in to monitor the serial readout. I have done some research and most point to it being just fine to connect both a battery and usb from pc at the same time.

I worry that this will let the magic smoke out. Soooooo before I summon the genie, is there a right and wrong way to do this? I could not find this answer directly and only have 2 boards left not in use, this and 1 extra.....

Thanks for helping a newbie out.

I'm not sure which motor controller you are using, but generally, the motor can (and often should) run on a separate power supply. The key point is to ensure that both power supplies share the same reference ground. Simply connect the grounds of the motor power supply and the controller power supply together to establish a common reference.

Awesome, this is exactly what I was missing when trying to get 1 battery to run the arduino and another to handle the motor. Previously the ESC just wouldn't respond to input from the PWM. Now that all works great.

Thank you for that. I thought just using the motor battery across the pot would be enough. By tying the grounds together I get the ESC to respond and run the motor.

Now to iron out the variation of input/output :slight_smile:

If I'm understanding things correctly, a variation in voltage to the pot can also cause a swing in value fed to the input, and I found this to be correct so far as my variance in Serial.print() was tighter when the pot was connected to Vcc from the arduino as compared to RAW voltage from the battery.

So by extention, keeping the same reference ground, I use the Vcc pin for a stable voltage to at least check that variable off the list without using a standalone voltage regulator which would consume more power.

Would a capacitor or other simple component smooth raw power enough to satisfy this parameter? I don't think there should be an issue with use of the Vcc off the board but if someone has a better idea, im open to it.

That change alone got rid of the majority of the fluctuations. Enough so that I no longer notice such variation in the noise of the motor constantly but only a small portion of say, 1min of elapsed time, where as with the pot connected directly to the raw voltage of the battery I would notice the variation via noise of motor a majority of that 1min of time.

I can actually live with this minor variance compared to before for my intended project which will be an R/C Airboat made with mostly scavenged parts. But I also still plan to test out some code that will average the potentiometer readings before feeding a PWM signal to the ESC.

Again, I am very thankful for the advise so far.

I didn't expect this to be more hardware bound than software considering that up until about an hour ago the pot was fed only from RAW voltage. The variation in motor speed when using RAW voltage is only noticed when running the longer code involving the display so I'm still a little stumped. I feel that averaging the data will iron out the rest of the minor variance im left with when using Vcc and may also correct the major variations I notice when using RAW voltage.

I retired from the HVAC field so Im not completely lost when it comes to logic of boards and executions of commands. Writing the code for them, well..... that's a bit different.

Code has been slightly modified to the below code

void setup() {
  Serial.begin(9600);
  ESC.attach(9,1000,2000); // (pin, min pulse width, max pulse width in microseconds) 
  display.begin(SSD1306_SWITCHCAPVCC, 0x3c);      //start the oled called display with a a hex addy of 0x3c
 

}

void loop() {
  potValue = analogRead(A0);                         //read the voltage on pin A0 and put the ADC value into variable level
   potRead = map(potValue, 0, 1023, 1000, 2000);   // scale it to use it with the servo library (Write microseconds command)
  ESC.writeMicroseconds(potRead);    // Send the signal to the ESC
convert = map(potValue, 140, 1023, 0, 128);            //convert the 0-1024 raw ADC to a more OLED friendly 1-128
percentvalue = map(potValue, 140, 1023, 0, 100);            //convert the 0-1024 raw ADC to a percent value

Now I am commanding the ESC with what I believe is a more direct PWM conversion than using the 0-180 of a standard servo, I'm doing some digging now to see what the library does behind the scenes with the PWM to drive a typical servo to better understand my changes. These changes don't seem to have changed the outcome much but Serial.print is giving me a slightly tighter variance now in readout. I believe this is due to the conversion from 0-1023(pot) to microseconds(1000-2000) compared to converting the potentiometer to the 0-180 for servo control.

I assume they use another mapping behind the scenes for converting the 0-180 to microsecond PWM in the Servo library but I I've googled many ways and I'm not certain how to serial.print the PWM signal without manually mapping it which I feel might skew my results. I do not have an oscilloscope to test the PWM signal so I'm not sure I can get an accurate answer without it.

Any feedback on this?

I have smoothed out the analog signal using a float value and averaging. That part is great and there is no longer any fluctuations in the motor speed.

I have however ran into a major issue, especially now that I have added this float/averaging.

I have found that the I2C OLED is causing major slow down in the code. I assume due to how often I am writing to the display.

Would an SPI based display potentially solve the issue of the buffer filling up and slowing down the code processing?

As I get some things worked out I will be separating the motor and display to two different arduinos.

The display will go in a small controller, which I will employ much of the same code to read and communicate the potentiometer/joystick on the controller to drive the motor on the boat.

I believe the issue will still present itself at that time so I am looking for early fixes before dealing with the display causing lag when sending data via RF

I did not have time to analyze your code. I had a similar problem with LCDs, and my solution was to avoid clearing the display entirely. Instead, I update it one full line at a time, which significantly improved performance. I update the display every 100 milliseconds or so.

Although I don’t have a motor, my system is busy communicating on the CAN bus. Additionally, I use two 4x20 displays treated as a single 8x20 display. I avoid using delay() except during the setup phase.

I have avoided delay entirely so far, even during serial.print testing.

Clearing the display would only have to happen on the deceleration if i get rid of the percent value display i suppose so i guess that might speed things up