Why does my speed variable not incriment correctly?

Ive been working on a project that would allow me to adjust the speed of a motor using an ESC and a lcd keypad shield. I'm perplexed as to why my speed variable will not go up or down in increments of 10. It jumps wildly around.

Everything up until this point has been paste and upload, so I really want to be successful with this.
I have no coding experience other than reading examples and others sketches.
In fact this was my first attempt and used two source codes and tried to combine them.

a simple equation of sm1 = (sm1 -10) doesn't seem to do anything different than sm1-=10

Edit: I now understand that this is the same equation in coding. Thank you septillion

#include <Servo.h>
#include <LiquidCrystal.h>

Servo m1;
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); //These are the pins used on this shield

#define btnRIGHT  0
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5

int lcd_key       = 0;
int adc_key_in    = 0;
int adc_key_prev  = 0;
int sm1           = 90; //SPEED OF MOTOR 1 



void setup ()
{

  //LCD START
  lcd.begin(16,2); // Start LCD
  lcd.setCursor(0,0);
  lcd.print ("Speed =          ");
  lcd.setCursor(9,0);
  lcd.print (sm1);

  //ESC SETUP
  m1.attach(11); //attach to pin 11 (digital)
  delay(100); 
  m1.write(10); //writing the minimum value (Arming it)
  delay(1500); //delay to let it process that
  m1.write(180); //Writes max value
  delay(1500); 
  m1.write(90); //writes neutral
  delay(1500); 
  m1.write(10); //writing the minimum value 
  delay(1500); 
  m1.write(90); //motor speed max is 180 min is 90  best 96><170.  90 is Neutral.
}

void loop() 
{
  m1.write(sm1);
  adc_key_prev = lcd_key ;       // Looking for changes
  lcd_key = read_LCD_buttons();  // read the buttons                       
  
  if (adc_key_prev != lcd_key)
  {
    lcd.setCursor(9,0); 
    lcd.print("      ");         // Blank, display returned Motor Speed 10-180
    lcd.setCursor(9,0); 
    lcd.print (sm1);
  }

  lcd.setCursor(5,1);            // move to the beginning of the second line

  switch (lcd_key)               // depending on which button was pushed, we perform an action
  {
  case btnRIGHT:
    {
      lcd.print("NA       ");
      break;
    }
  case btnLEFT:
    {
      lcd.print("NA       ");
      break;
    }
  case btnUP:
    {
      lcd.print("UP +10   ");
      sm1+=10; // WHERE I ADJUST SPEED UP
      break;
    }
  case btnDOWN:
    {
      lcd.print("DOWN -10 ");
      sm1-=10; // WHERE I ADJUST SPEED DOWN
      break;
    }
  case btnSELECT:
    {
      lcd.print("NEUTRAL  "); // STOP MOTOR
      sm1=  90;
      break;
    }
  case btnNONE:
    {
      lcd.print("WAITING  ");
      break;
    }
  }/* --(end switch )-- */
    
}
int read_LCD_buttons()
{
  adc_key_in = analogRead(0);      // read the value from the sensor 
  delay(5); //switch debounce delay. Increase this delay if incorrect switch selections are returned.
  int k = (analogRead(0) - adc_key_in); //gives the button a slight range to allow for a little contact resistance noise
  if (5 < abs(k)) return btnNONE;  // double checks the keypress. If the two readings are not equal +/-k value after debounce delay, it tries again.
  // my buttons when read are centered at these valies: 0, 144, 329, 504, 741
  // we add approx 50 to those values and check to see if we are close
  if (adc_key_in > 1000) return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result
  if (adc_key_in < 50)   return btnRIGHT;  
  if (adc_key_in < 195)  return btnUP;
  if (adc_key_in < 380)  return btnDOWN; 
  if (adc_key_in < 555)  return btnLEFT; 
  if (adc_key_in < 790)  return btnSELECT;   
  return btnNONE;  // when all others fail, return this
}

First off some tips

Please edit your post and replace the quote tags with code tags. See How to use this forum.

Second, sm1 is just a terrible variable name. The comment tells me its the speed of motor 1. Then just call it motor1Speed. Or even better, because assuming you want to have more speeds because of the number, make an array with all the speeds called motorSpeeds. It's better to have longer but self explaining names then just short ones. Way easier to remember and way clearer to read by others and future you :wink:

Same for m1, call it motor, that's what it is. Or again, an array motors (plural).

adc_key_in = analogRead(0);

Hard coding pins is considered bad practice. Now it's:

  • Hard to change
  • Hard to understand, is A0 indeed the pin for the buttons?
  • Easy to mix up, maybe you accidentally use A1 later on

It's more a style thing but the "Arduino standard" is to use myVariableName or myFunctionName for varaibles and functions instead on my_name etc. And use ALL_CAPS for macros (#define).

charlespclements:
ps a simple equation of sm1 = (sm1 -10) doesn't seem to do anything different than sm1-=10

Correct, that's how it's designed. The second is just shorthand for the first. What did you expect?

And I find the read_LCD_buttons a bit weird. The function only returns a value if the button changed state in those 5ms of the delay(). Pretty easy to miss a press that way.

And although not wrong the order in the loop is a bit weird. But because it loops it's not a problem but now you do

  • Assign previous key press
  • Write motor speed
  • Check for button
  • Change motor speed

Where it's more logical to set the motor speed after you changed it

  • Check buttons
  • Assign previous key
  • Change motor speed
  • Write motor speed

And that weird ordering is indirect giving you the trouble. You only print the speed on the screen when you pressed a button. But you check what to do for a button all the time, even if it's the same button as last time. So more the switch inside the check for change (you only want to do something when the key state was changed aka a new button because pressed). Also, It would be more logical to first check the button and alter the speed and then print the speed to the screen. Now you first print the old speed and then checking the button and changing the speed. The LCD now shows the old speed.

When you press a switch, loop() reads that you have pressed a switch. The question is how many times the switch is read each time you press a switch. If you think that the answer is once, you would be wrong.

The state change detection example shows how to determine that a switch has become pressed, rather than is pressed. You want to increment or decrement only when the switch becomes pressed (or becomes released), not when the switch is pressed.

I think I've got it. It took a while to step back and ask myself what I wanted this program to do. I finally just erased everything and took another stab at it with my previous mistakes in mind. Please tell me if you have any suggestions.

/*
  LCD KEYPAD SHIELD ESC/SERVO CONTROLLER
  Sends signals to an ESC controlling a dc motor, allowing for variable speed and shaft rotation.
  Written by Charles P Clements
  Free to use, and distribute.
  Please repost any major changes or additions to the Arduino.cc forum.

            The circuit
   LCD RS pin to digital pin 8
   LCD Enable pin to digital pin 9
   LCD D4 pin to digital pin 4
   LCD D5 pin to digital pin 5
   LCD D6 pin to digital pin 6
   LCD D7 pin to digital pin 7
   LCD BL pin to digital pin 10

   KEY pin to analog pin 0

   ESC Signal to digital pin 11
   ESC Ground to ground

*/


#include <Servo.h>
#include <LiquidCrystal.h>
#include <LCDKeypad.h>


Servo motor;
LCDKeypad lcd;


static int motorSpeed           = 90; //speed of motor


void setup ()
{

  //LCD START
  lcd.begin(16, 2);      // start LCD
  lcd.clear();
  lcd.setCursor(0, 0);


  //ESC SETUP
  //motor speed max is 180 min is 90  best 96><170
  lcd.print ("ESC SETUP   ");
  delay(500);
  motor.attach(11);             //attach to pin 11 (digital)
  delay(500);                   //just a delay to let stuff get started
  motor.write(10);              //writing the minimum value (arming it)
  lcd.setCursor(0, 0);
  lcd.print ("MIN         ");
  delay(1500);                  //delay to let it process that
  motor.write(180);             //writes max value
  lcd.setCursor(0, 0);
  lcd.print ("MAX         ");
  delay(1500);                  //delay to let it process
  motor.write(90);              //writes neutral
  lcd.setCursor(0, 0);
  lcd.print ("NEUTRAL     ");
  delay(1500);//delay           //again waiting
  //at this point most ESC's are armed but some may need this additional step
  motor.write(10);              //back to min value
  lcd.setCursor(0, 0);
  lcd.print ("MIN         ");
  delay(1500);//delay
  motor.write(90);
  lcd.print("COMPLETE     ");
}

void loop()
{
  lcd.setCursor(0, 0);
  lcd.print ("Speed =          ");
  lcd.setCursor(9, 0);
  lcd.print (motorSpeed);
  lcd.setCursor(0, 1);

  switch (lcd.button())
    //the code I needed in my previous version for a single key press.
    //previously key press was affected by how many times
    //the arduino sampled the analog pin 0 during a single key click/press

  {
    case KEYPAD_LEFT:
      if (motorSpeed >= 1) {
        lcd.print("   Left -1      ");
        motorSpeed -= 1;
        delay(200);
      } else {
        lcd.print("    NOT ABLE    ");
      }
      break;
    case KEYPAD_RIGHT:
      if (motorSpeed <= 179) {
        lcd.print("    Right +1    ");
        motorSpeed += 1;
        delay(200);
      } else {
        lcd.print("    NOT ABLE    ");
      }
      break;
    case KEYPAD_DOWN:
      if (motorSpeed >= 10) {
        lcd.print("   Down -10     ");
        motorSpeed -= 10;
        delay(200);
      } else {
        lcd.print("    NOT ABLE    ");
      }
      break;
    case KEYPAD_UP:
      if (motorSpeed <= 170) {
        lcd.print("   Up +10       ");
        motorSpeed += 10;
        delay(200);
      } else {
        lcd.print("    NOT ABLE    ");
      }
      break;
    case KEYPAD_SELECT:
      lcd.print("Reset to Neutral");
      motorSpeed = 90;
      delay(200);
      break;
      delay (100);
  }
  motor.write(motorSpeed);

}

Fist of all, it's not considered very nice to skip answering questions asked about the problem :wink:

But the first thing I notice when looking at the code, and to quote myself:

septillion:
Hard coding pins is considered bad practice.

And your code already almost contains a nice variable list for the pins. Only now it's comment. But just make it code and it's as readable as now but now you can use names instead of numbers in the rest of the code which will make it way more readable.

//* The circuit
const byte LcdRsPin = 8;
const byte LcdEnPin = 9;
const byte LcdD4Pin = 4;
const byte LcdD5Pin = 5;
const byte LcdD6Pin = 6;
const byte LcdD7Pin = 7;
const byte LcdBacklightPin = 10;
const byte KeyPin = A0;
const byte MotorPin = 11;

Next, a static at global level is useless :wink:

And I doubt the LCD works without setting the pins (unless it defaults to those but I would explicitly call the pin's (by name!)).

And you still print the speed first and then check if you want to update it. So the printed speed is lacking.

And then the error, you don't check for a change in key. And the loop will run very very fast (which it should!) so a short press of a key will trigger that task not only once (for instead increase the speed) but a lot of times! So a short press will lead to a big change in speed and not just a difference of 10. That was something you did check in the first code.

So please review my last post (and answer the questions) and try to follow the answers / hint in there. :slight_smile:

Septillion please forgive me if I missed anything.

I'm really trying to implement your "standard" programing techniques, but some things are just above my level at the moment without an example code to look at. Please don't see this as a request to overhaul my code, as I've already learned a lot from you about proper coding form/etiquette.

If I may,

Variable names are important, it should be easy to determine what the variable actually represents.

Not hardcoding means to give the pin a variable name to be used inside of code. Correct?

The LCD does work with the default library pin coding, and the keypad library I believe is similar to my first code, though I cant be certain.
I would like to be able to read the code that's in both library's in order to understand what it is doing.
when I try

LCDKeypad lcd (LcdRsPin, LcdEnPin, LcdD4Pin, LcdD5Pin, LcdD6Pin, LcdD7Pin);

I get this error,

LCD_KEYPAD_SHIELD_ESC_SERVO_CONTROLLER:39: error: no matching function for call to 'LCDKeypad::LCDKeypad(const byte&, const byte&, const byte&, const byte&, const byte&, const byte&)'

LCDKeypad lcd (LcdRsPin, LcdEnPin, LcdD4Pin, LcdD5Pin, LcdD6Pin, LcdD7Pin);

leading me to believe I'll need to update the library

As far as I see your only real question is if A0 really the pin for key press. It is. If this is an issue with hardcoding, see my question about hardcoding.

Is the debounce code supposed to be implemented in the library or is it something that can wrap the entire switch statement?

As for an array, I'm not sure how one works, either in theory or its use.

I am no longer calling directly on A0 pin but I can't speak for the library's and how their pins are defined. In order for me to use variable names for pins I believe I would have to edit these as
My code now only defines motorSpeed.

As the code runs now

-ESC setup is run outside of loop(Min,Max,Neutral,Min,Neutral)(Should these be variables? I'm back to the hardcoding question there.)

-Check switch (Delay allows for only one button press each +- half second, plenty of time for a one time read, but can still increment easily without clicking 18 times to scroll the entire variable range. Thank you PaulS for the delay idea. Athough acting on a state change was not I was aiming for it was the right advice to get me thinking)

-Check for allowed variable values

-Print switch

-Write to motor

-Print speed

It works, but I want it to be easy for others to use as well, is that what the constraints at the beginning are for, easy setup variable changes throughout the code with only one change to initial value?

Tell me if this is any better, and I believe a, Thank You septillion is in order.

/*
  LCD KEYPAD SHIELD ESC/SERVO CONTROLLER
  Sends signals to an ESC controlling a dc motor, allowing for variable speed and shaft rotation.
  Written by Charles P Clements
  Free to use, and distribute.
  Please repost any major changes or additions to the Arduino.cc forum.

            The circuit
   LCD RS pin to digital pin 8
   LCD Enable pin to digital pin 9
   LCD D4 pin to digital pin 4
   LCD D5 pin to digital pin 5
   LCD D6 pin to digital pin 6
   LCD D7 pin to digital pin 7
   LCD BL pin to digital pin 10 (NOT USED PIN10 IS DANGEROUS FOR ARDUINO ADC IF NOT SET RIGHT

   KEY pin to analog pin 0

   ESC Signal to digital pin 11
   ESC Ground to ground

*/

#include <Servo.h>
#include <LiquidCrystal.h>
#include <LCDKeypad.h>

//const byte LcdRsPin = 8;  //left here until I can use it.
//const byte LcdEnPin = 9;
//const byte LcdD4Pin = 4;
//const byte LcdD5Pin = 5;
//const byte LcdD6Pin = 6;
//const byte LcdD7Pin = 7;

const byte KeyPin = A0;
const byte MotorPin = 11;

int motorSpeedMin       = 10;
int motorSpeedNeutral   = 90;
int motorSpeedMax       = 180;
int motorSpeed          = motorSpeedNeutral;        //Speed of motor initiates at neutral.
int buttonScrollDelay   = 200;                      //Changes scroll rate when button is held down.

Servo motor;
LCDKeypad lcd;

void setup ()
{

  //LCD START
  lcd.begin(16, 2);                  // start LCD
  lcd.clear();
  lcd.setCursor(0, 0);


  //ESC SETUP
  //motor speed max is 180 min is 90 reverse is min 90 max 10 
  lcd.print ("ESC SETUP   ");
  delay(500);
  motor.attach(MotorPin);            //attach to pin 11 (digital)
  delay(500);                        //just a delay to let stuff get started
  motor.write(motorSpeedMin);        //writing the minimum value (arming it)
  lcd.setCursor(0, 0);
  lcd.print ("MIN         ");
  delay(1500);                       //delay to let it process that
  motor.write(motorSpeedMax);        //writes max value
  lcd.setCursor(0, 0);
  lcd.print ("MAX         ");
  delay(1500);                       //delay to let it process
  motor.write(motorSpeedNeutral);    //writes neutral
  lcd.setCursor(0, 0);
  lcd.print ("NEUTRAL     ");
  delay(1500);//delay                //again waiting

  //at this point most ESC's are armed but some may need this additional step

  motor.write(motorSpeedMin);        //back to min value
  lcd.setCursor(0, 0);
  lcd.print ("MIN         ");
  delay(500);
  motor.write(motorSpeedNeutral);    //Neutral
  lcd.setCursor(0, 0);
  lcd.print("COMPLETE     ");
  delay(1000);
}

void loop()
{
  switch (lcd.button())
  {
    case KEYPAD_LEFT:
      if (motorSpeed >= 1) {
        lcd.print("   Left -1      ");
        motorSpeed -= 1;
        delay(buttonScrollDelay);
      } else {
        lcd.print("    NOT ABLE    ");
      }
      break;
    case KEYPAD_RIGHT:
      if (motorSpeed <= 179) {
        lcd.print("    Right +1    ");
        motorSpeed += 1;
        delay(buttonScrollDelay);
      } else {
        lcd.print("    NOT ABLE    ");
      }
      break;
    case KEYPAD_DOWN:
      if (motorSpeed >= 10) {
        lcd.print("   Down -10     ");
        motorSpeed -= 10;
        delay(buttonScrollDelay);
      } else {
        lcd.print("    NOT ABLE    ");
      }
      break;
    case KEYPAD_UP:
      if (motorSpeed <= 170) {
        lcd.print("   Up +10       ");
        motorSpeed += 10;
        delay(buttonScrollDelay);
      } else {
        lcd.print("    NOT ABLE    ");
      }
      break;
    case KEYPAD_SELECT:
      lcd.print("Reset to Neutral");
      motorSpeed = 90;
      delay(buttonScrollDelay);
      break;

      delay (100);
  }
  motor.write(motorSpeed);
  lcd.setCursor(0, 0);
  lcd.print ("Speed =          ");
  lcd.setCursor(9, 0);
  lcd.print (motorSpeed);
  lcd.setCursor(0, 1);
  delay(100);
}

leading me to believe I'll need to update the library

Why? The library has a constructor that takes non-const values, but you are supplying const values. You could change the library to take const values. Or, you could drop the const keyword in the sketch. Or, you could cast the const value to a non-const value in the call.

The second option seems easiest to me. The first is what the library authors should have done.

PaulS:
Why? The library has a constructor that takes non-const values, but you are supplying const values.

You could change the library to take const values....what the library authors should have done.

Why? Because I would like to understand the mechanics of the library.
I'm learning as I do, and posting for criticism, which is best for me.

I believe that in the spirit of this thread and what "should" be done, I would rather take the harder route and try to edit the library. That is all I meant by updating it.

Again, any suggestions for the current code, or how to go about editing the library code are welcome. Please don't hesitate to tell me I'm going about this wrong.

Again, any suggestions for the current code, or how to go about editing the library code are welcome.

When I go about editing a library, I use notepad++. But, any editor will work.

Should I unzip the files, edit, and recompile all files with a completely new name for testing.

I don't want to mess up any other library's that I have installed. I seemed to have done that at least once before by trying different LCD/ keypad library's. I ended up with 3 different versions of keypad.h.

Thank you PaulS

Should I unzip the files, edit, and recompile all files with a completely new name for testing.

Seems like a reasonable thing to do.

But keep in mind that, unless you are the maker of the library, you cannot update the public version of the library resulting. As a result others with the same library but unedited cannot use your code (that uses the library). But if the library is on git you can try to make a pull request to incorporate your edit into the public library.

charlespclements:
Not hardcoding means to give the pin a variable name to be used inside of code. Correct?

Yes, then it's not buried and scattered in the code but accessible (and editable) in a single place.

charlespclements:
The LCD does work with the default library pin coding, and the keypad library I believe is similar to my first code, though I cant be certain.

Mm, I don't like it when a library just uses pins. Makes it look like magic (aka, have to open the library to know what it does). Even though you connect it to the default pins I would call explicitly to make clear what it uses.

charlespclements:
I get this error,

Mm, yeah, that's the library not liking const :confused: I would say that's a shortcoming of the library. But I should not bother editing the library. I think I would skip the list of const pin variables and just add the declaration of the LCDkeypad object next to the pin map part of the code, use the pin numbers in that and add comment like

//LCDKeypad(RsPin, EnPin, D4Pin, D5Pin, D6Pin, D7Pin)

charlespclements:
Is the debounce code supposed to be implemented in the library or is it something that can wrap the entire switch statement?

Nothing is supposed to be. They could have done it but if not, you can just wrap it in a function to do so.

charlespclements:
As for an array, I'm not sure how one works, either in theory or its use.

They are simple, just have a look. Instead of

int motor1;
int motor2;
//you do
int motors[2]; //plural
//and call it like
motors[0];
//and 
motor[1];

charlespclements:
-ESC setup is run outside of loop(Min,Max,Neutral,Min,Neutral)(Should these be variables? I'm back to the hardcoding question there.)

I would indeed make them const variables. Makes it easy to change all Min, Max etc. But wouldn't call them just min, max etc. min/max/... of what?

charlespclements:
-Check switch (Delay allows for only one button press each +- half second, plenty of time for a one time read, but can still increment easily without clicking 18 times to scroll the entire variable range. Thank you PaulS for the delay idea. Athough acting on a state change was not I was aiming for it was the right advice to get me thinking)

But keep in mind the delay() will block you from acting on anything. So you can only do something 10 times a second. Delay()'s make it a bit hard to extend code.

charlespclements:
It works, but I want it to be easy for others to use as well, is that what the constraints at the beginning are for, easy setup variable changes throughout the code with only one change to initial value?

Yep

Did I miss something?