Hi,
I am a newcomer trying to make / set a motor controller for a project that should move a tank in cycles. The hardware elements for the basic testing are:
- Arduino UNO
- IBT-2 controller
- LCD 2IC
- keypad
- external 5V supply for the motor
The configurations is the standard I have found in this forum and elsewhere (e.g., dronebotworkshop. For the test, I am using an small motor but later a large one is to be used.
I was grateful to find a nice code written by @sterretjeToo (do not know how to link). I modified the code to include, as suggested a stop and restart states. The code compiles and runs but has a bug. To start the motor I should press "A". This does not happens at the beginning. The motor immediately starts after the time is inserted. In the second reintroduction of parameters, it works as it should.
Original source of the controller I found in this post Original post:
I have no idea what causes this error. I need to say that I am also not an experienced C programmer. I write other kinds of algorithms mostly in Fortran and other languages.
Perhaps, someone could provide me a hint(s) to solve this issue. The potentiometer appearing in the picture is not connected. The hookup of the components is given in the code below (I deleted the compiler directives of the original code to be able to compile in my system (Arduino IDE 2.2.1 in macOs Sonoma 14.1.2 ).
I would like to say that I am really impressed about the sources and valuable information I found in this Forum. Thanks for sharing! I started with my new Arduino a week ago and with the help found in discussions of this Forum, I have been able to progress with my project. I have learned a lot. It is really amazing what you do! Kudos to all.
// source: https://forum.arduino.cc/t/control-speed-and-timing-for-dc-motor-using-keypad-input-and-shown-on-lcd-disply/385810/11
// sterretjeToo, Karma: 2000+
const byte ST_DISPLAY_MAINMENU=0;
const byte ST_WAIT=1;
const byte ST_SETSPEED=2;
const byte ST_SETTIMING=3;
const byte ST_STARTMOTOR=4;
const byte ST_IDLE=5;
// options to control the motor
int MOTOR_CONTINUE=0;
int MOTOR_START=1;
int MOTOR_FORCESTOP=2;
//int motorPin = 3;
// Motor (both PWM)
const int RPWM = 10;
const int LPWM = 11;
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
// LCD
const byte ROWS = 4; // Constants for row and column sizes
const byte COLS = 4;
const int I2C_addr = 0x27; // Define I2C Address - found with scanner
// Keypad
char hexaKeys[ROWS][COLS] = { // Array to represent keys on keypad
{ '1', '2', '3', 'A' },
{ '4', '5', '6', 'B' },
{ '7', '8', '9', 'C' },
{ '*', '0', '#', 'D' }
};
// byte rowPins[ROWS] = {10, 9, 8, 7}; //connect to the row pinouts of the keypad
// byte colPins[COLS] = {13, 12, 11}; //connect to the column pinouts of the keypad
// initialize the library with the numbers of the interface pins
// LCD
const int en = 2, rw = 1, rs = 0, d4 = 4, d5 = 5, d6 = 6, d7 = 7, bl = 3;
// Keypad
byte rowPins[ROWS] = { 9, 8, 7, 6 };
byte colPins[COLS] = { 5, 4, 3, 2 };
Keypad keypad = Keypad( makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS );
//char NO_KEY=0;
//Keypad kdp = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);
LiquidCrystal_I2C lcd(I2C_addr, en, rw, rs, d4, d5, d6, d7, bl, POSITIVE);
// the state of our application; start with main menu display
byte currentState = ST_DISPLAY_MAINMENU;
// speed and timing variables
// both are global so we can display them in the main menu
int theSpeed = 0;
long theTiming = 0;
// posts # 21 & 22
void setup() {
Serial.begin(9600);
//pinMode(motorPin, OUTPUT);
pinMode(RPWM, OUTPUT); // set motor connections as outputs
pinMode(LPWM, OUTPUT);
// set up the LCD's number of columns and rows:
lcd.begin(20, 4);
Serial.println("20x4 display");
}
void loop()
{
// single key from keypad
char key;
// text from keypad
char *text;
// let the motor do what it was doing
runMotor(MOTOR_CONTINUE);
//Serial.print("Speed: "); Serial.println(theSpeed);
switch (currentState)
{
case ST_DISPLAY_MAINMENU:
// display main menu
displayMenu();
// switch to wait state
currentState = ST_WAIT;
break;
case ST_WAIT:
// get key
key = getKeyWithEcho();
// if speed setting selected
if (key == '1')
{
// display speed 'menu'
displaySpeedMenu();
// change to state where user can enter speed
currentState = ST_SETSPEED;
}
// if timing setting selected
if (key == '2')
{
// display 'timing' menu
displayTimingMenu();
// change to state where user can enter timing
currentState = ST_SETTIMING;
}
// if START key is selected
if (key == 'A')
{
// display main menu
displayMenu();
// change to state where user can start the motor
currentState = ST_STARTMOTOR;
}
// if END key is selected
if (key == 'D')
{
// display main menu
displayMenu();
// change to state where user can reset
currentState = ST_IDLE;
}
// Note: state does not change if entry is not '1', '2', 'A'
break;
case ST_SETSPEED:
// get the text entered on the keypad
text = getKeypadText();
// if text complete
if (text != NULL)
{
// if user did enter a speed
if (text[0] != '\0')
{
theSpeed = atoi(text);
}
currentState = ST_DISPLAY_MAINMENU;
}
break;
case ST_SETTIMING:
// get the text entered on the keypad
text = getKeypadText();
// if text complete
if (text != NULL)
{
// if user did enter a speed
if (text[0] != '\0')
{
theTiming = atoi(text);
// NEW (LS) originaly here was the start of the motor (moved to ST_STARTMOTOR)
}
currentState = ST_DISPLAY_MAINMENU;
}
break;
// new (LS)
case ST_STARTMOTOR:
// start motor
runMotor(MOTOR_START);
currentState = ST_DISPLAY_MAINMENU;
break;
case ST_IDLE:
// stop motor
runMotor(MOTOR_FORCESTOP);
currentState = ST_DISPLAY_MAINMENU;
break;
} // end_of_switch
}
/*
display a title on the first line of the display; it always clears the LCD
*/
void displayTitle()
{
// clear the lcd
lcd.clear();
// print the project title on the first line
lcd.setCursor(0, 0);
lcd.print("Speed controller");
}
/*
display main menu
*/
void displayMenu()
{
// current line where to write on LCD; every time that we write, currentLine will be incremented
byte currentLine = 0;
// text buffer for 20 characters and string terminator
char textbuffer[21];
// display the title on the first line and update currentLine
displayTitle();
currentLine = 1;
// print the current settings on the second line
lcd.setCursor(0, currentLine++);
sprintf(textbuffer, "S = %d, T = %d", theSpeed, theTiming);
lcd.print(textbuffer);
// print the first menu option on the third line
lcd.setCursor(0, currentLine++);
lcd.print("1 Set speed");
// print the second menu option on the fourth line
lcd.setCursor(0, currentLine++);
lcd.print("2 Set time");
}
/*
display a 'menu' where the user can enter a speed
*/
void displaySpeedMenu()
{
// display the title on the 1st line
displayTitle();
// display additional info on 3rd line
lcd.setCursor(0, 2);
lcd.print("# Finish, * Cancel");
lcd.setCursor(0, 3);
lcd.print("A Start, D STOPl");
// prompt user to set speed (2nd line)
lcd.setCursor(0, 1);
lcd.print("Set speed: ");
}
/*
display a 'menu' where the user can enter a timing
*/
void displayTimingMenu()
{
// display the title on the 1st line
displayTitle();
// display additional info on 3rd line
lcd.setCursor(0, 2);
lcd.print("#Finish, *Cancel");
// prompt user to set speed (2nd line)
lcd.setCursor(0, 1);
lcd.print("Set time: ");
}
/*
get a keystroke from the keypad
echo the key that was pressed to the LCD
the echo will be on the current position of the LCD
returns
the key that was pressed or NO_KEY
*/
char getKeyWithEcho()
{
// read a key
char key = keypad.getKey();
// if no key pressed
if (key != NO_KEY)
{
// for debugging, output to serial monitor
Serial.print("Key: "); Serial.println(key);
// display on current position of LCD
lcd.print(key);
}
return key;
}
/*
get a text from the keypad (max 20 characters)
'#' finishes the entry of data
'*' cancels the entry of data
returns
NULL if not complete
empty text if canceled
entered tet (might be empty)
*/
char *getKeypadText()
{
// a buffer for 20 keypresses and one
static char keypadbuffer[21];
// index in above buffer
static char index = 0;
// read a key
char key = getKeyWithEcho();
// if nothing pressed
if (key == NO_KEY)
{
// indicate no complete data
return NULL;
}
// if 'cancel' key
if (key == '*')
{
// reset index
index = 0;
// create empty text
keypadbuffer[index] = '\0';
// return text
return keypadbuffer;
}
// if 'enter' key
if (key == '#')
{
// add a string terminator
keypadbuffer[index] = '\0';
// reset index for next time
index = 0;
// return the text
Serial.println(keypadbuffer);
return keypadbuffer;
}
// check for buffer overflow
if (index >= sizeof(keypadbuffer))
{
// add a string terminator
keypadbuffer[sizeof(keypadbuffer) - 1] = '\0';
// reset index for next time
index = 0;
// return the text
return keypadbuffer;
}
// add the character to the buffer
keypadbuffer[index++] = key;
// indicate that text is not complete
return NULL;
}
/*
starts or stops the motor or runs the motor for a given time
parameter
option: option to control motor
MOTOR_FORCESTOP: force an (emergency) stop; oberrides any other options
MOTOR_START: if not started, start it; if already started, it's ignored
for any other option value, motor keeps on doing what it was doing
*/
void runMotor(byte options)
{
// the 'time' that the motor started running
static unsigned long motorRunStarttime = 0;
// if FORCESTOP received
if ((options & MOTOR_FORCESTOP) == MOTOR_FORCESTOP)
{
// for debugging
Serial.println("FORCESTOP received");
// stop the motor
//analogWrite(motorPin, 0);
analogWrite(RPWM, 0); // Stop = 0
analogWrite(LPWM, 0);
// reset start 'time'
motorRunStarttime = 0;
// nothing more to do
return;
}
// if START received and motor not started yet
if ((options & MOTOR_START) == MOTOR_START && motorRunStarttime == 0)
{
// for debugging
Serial.println("START received while motor was not running");
Serial.print("Speed: "); Serial.println(theSpeed);
Serial.print("Timing: "); Serial.println(theTiming);
// set the start 'time'
motorRunStarttime = millis();
// display the start time
Serial.print("Time: "); Serial.println(motorRunStarttime);
}
// if the motor has run for the specified duration
if (millis() - motorRunStarttime >= (theTiming * 1000UL))
{
// inform the user that motor has stopped
if (motorRunStarttime != 0)
{
Serial.println("Motor stopped");
Serial.print("Time: "); Serial.println(millis());
}
// stop the motor;
//analogWrite(motorPin, 0);
analogWrite(RPWM, 0); // Stop = 0
analogWrite(LPWM, 0);
// reset the start 'time'
motorRunStarttime = 0;
}
else
{
// set the motor speed
if (theSpeed >= 0 && theSpeed <= 255)
{
//analogWrite(motorPin, theSpeed);
analogWrite(LPWM, theSpeed);
}
}
}
type or paste code here