I've been creating my first Arduino project to control a motor with two push buttons that toggle on/off and direction. i.e. button 1 goes fwd, button 2 goes in rev and a second press on each stops the motor.
The Arduino outputs a serial signal via pin 1 to a comercial driver board. This basically sends a speed and direction command using a library from the manufacturer.
Everything is functioning well but I'm having trouble getting my head around using millis to create a delay.
I want the motor to start at a slow speed wait 2-3 seconds then jump to a higher speed until stopped. I've tried lots of things to no avail and currently I've got an elapsedMillis library to provide a delay. The problem is the motor just doesn't change speed.
Hopefully you guys wont shoot me down in flames for being so inept.
Here's my code
const int FWDbuttonPin = 2;
const int REVbuttonPin = 7;
const int FWDledPin = 12;
const int REVledPin = 13;
#include <SyRenSimplified.h>
SyRenSimplified SR;
#include <elapsedMillis.h>
elapsedMillis timeElapsedFWD;
elapsedMillis timeElapsedREV;
long interval = 2000;
int FWDledState = LOW;
int FWDbuttonState;
int FWDlastButtonState = LOW;
int REVledState = LOW;
int REVbuttonState;
int REVlastButtonState = LOW;
long lastDebounceTime = 0;
long debounceDelay = 50;
void setup() {
SyRenTXPinSerial.begin(9600);
pinMode(FWDbuttonPin, INPUT);
pinMode(FWDledPin, OUTPUT);
pinMode(REVbuttonPin, INPUT);
pinMode(REVledPin, OUTPUT);
digitalWrite(FWDledPin, FWDledState);
digitalWrite(REVledPin, REVledState);
}
void loop() {
//Forward operation
//Debounce and toggle button
int FWDreading = digitalRead(FWDbuttonPin);
if (FWDreading != FWDlastButtonState) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
if (FWDreading != FWDbuttonState) {
FWDbuttonState = FWDreading;
if (FWDbuttonState == HIGH) {
FWDledState = !FWDledState;
}
}
}
digitalWrite(FWDledPin, FWDledState);
//Send forward signal to motor
if (FWDledState == HIGH){
REVledState = LOW;//Make sure reverse signal is cancelled
SR.motor(-10);
if (timeElapsedFWD > interval)
SR.motor(-50);
timeElapsedFWD = 0;
}
FWDlastButtonState = FWDreading;
//Reverse operation
//Debounce and toggle button
int REVreading = digitalRead(REVbuttonPin);
if (REVreading != REVlastButtonState) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
if (REVreading != REVbuttonState) {
REVbuttonState = REVreading;
if (REVbuttonState == HIGH) {
REVledState = !REVledState;
}
}
}
digitalWrite(REVledPin, REVledState);
//Send reverse signal to motor
if (REVledState == HIGH){
FWDledState = LOW;//Make sure forward signal is cancelled
SR.motor(10);
if (timeElapsedREV > interval)
SR.motor(50);
timeElapsedREV = 0;
}
REVlastButtonState = REVreading;
//Neutral State
if (REVledState == LOW && FWDledState == LOW){
SR.motor(0);
}
}
You might also get some ideas from the Thread planning and implementing a program. Organizing code into functions makes it much easier to separate the control logic (what, and when) from the action logic (how).
For example if you put all the button reading into one function a single debounce process would be sufficient - and I strongly suspect you will find that no explicit debouncing is needed.
You will also need a variable to keep track of where you are in the motor cycle - for example OFF, Acclerating and Full speed could be represented by the characters O, A and F.
I have in mind a code structure like this
void loop() {
curMillis = millis(); // one value for use throughout the loop
readButtons();
commandMotor();
}
The commandMotor() function would check the state of the buttons, the state of the motor and the time to decide whether to call another function that just has the code to send instructions to the motor - that might be called driveMotor()
I've tried to create a more structured sketch as per the examples given but I'm having trouble with the commandMotor() and readButtons() functions. At the moment it's just spinning the motor on connection but I'm hoping it's a problem with the poor coding, not the structure. If someone could confirm the basic layout is suitable in structure and suggest where I might have issues I'd be very grateful.
// Libraries
#include <SyRenSimplified.h> // serial command library
SyRenSimplified SR;
// Constants
const int FWDbuttonPin = 2; // pin assignment
const int REVbuttonPin = 7;
const int FWD_Ramp_Interval = 2500; // slow motor speed periods, allows independent values
const int REV_Ramp_Interval = 2500;
// Variables
byte FWDbuttonstate = LOW;
byte REVbuttonstate = LOW;
int motorOff;
int motorSlowFWD;
int motorFastFWD;
int motorSlowREV;
int motorFastREV;
int motorstate = motorOff;
unsigned long currentMillis = 0; // stores the value of millis() in each iteration of loop()
unsigned long previousButtonMillis = 0; // time when button press last checked
void setup() {
SyRenTXPinSerial.begin(9600);
pinMode(FWDbuttonPin, INPUT);
pinMode(REVbuttonPin, INPUT);
}
void loop() {
currentMillis = millis(); // one value for use throughout the loop
readButtons(); // checks for button presses
commandMotor(); // decides what mode to set the motor to
driveMotor(); // sends the instructions to the motor
}
void readButtons() {
if (digitalRead(FWDbuttonPin) == LOW) {
FWDbuttonstate = ! FWDbuttonstate; // this changes it to LOW if it was HIGH
// and to HIGH if it was LOW
}
if (digitalRead(REVbuttonPin) == LOW) {
REVbuttonstate = ! REVbuttonstate; // this changes it to LOW if it was HIGH
// and to HIGH if it was LOW
}
}
void commandMotor() {
if (FWDbuttonstate == HIGH) { // forward operation
if (currentMillis <= FWD_Ramp_Interval) {
motorstate = motorSlowFWD;
previousButtonMillis += FWD_Ramp_Interval;
}
if (previousButtonMillis >= FWD_Ramp_Interval) {
motorstate = motorFastFWD;
}
REVbuttonstate = LOW; // reset reverse state
}
if (REVbuttonstate == HIGH) { // reverse operation
if (currentMillis <= REV_Ramp_Interval) {
motorstate = motorSlowREV;
previousButtonMillis += REV_Ramp_Interval;
}
if (previousButtonMillis >= FWD_Ramp_Interval) {
motorstate = motorFastREV;
}
FWDbuttonstate = LOW; // reset forward state
}
else { // neutral state
motorstate = motorOff;
}
}
void driveMotor() {
if (motorstate = motorOff) { SR.motor(0); }
if (motorstate = motorSlowFWD) { SR.motor(10); }
if (motorstate = motorFastFWD) { SR.motor(50); }
if (motorstate = motorSlowREV) { SR.motor(-10); }
if (motorstate = motorFastREV) { SR.motor(-50); }
}
I see the problem but now I'm just getting a stange noise from the motor
But, did you fix it?
Listen to the noise for a while. After a while it will no longer seem strange.
If that isn't useful advice (and I don't believe for a minute that it is), we need a lot more information starting with a link to the library you are using, a link to the motor/driver, and a schematic so we can see how the motor is powered.
// Libraries
#include <SyRenSimplified.h> // serial command library
SyRenSimplified SR;
// Constants
const int FWDbuttonPin = 2; // pin assignment
const int REVbuttonPin = 7;
const int FWD_Ramp_Interval = 2500; // slow motor speed periods, allows independent values
const int REV_Ramp_Interval = 2500;
// Variables
byte FWDbuttonstate = LOW;
byte REVbuttonstate = LOW;
int motorOff;
int motorSlowFWD;
int motorFastFWD;
int motorSlowREV;
int motorFastREV;
int motorstate = motorOff;
unsigned long currentMillis = 0; // stores the value of millis() in each iteration of loop()
unsigned long previousButtonMillis = 0; // time when button press last checked
void setup() {
SyRenTXPinSerial.begin(9600);
pinMode(FWDbuttonPin, INPUT);
pinMode(REVbuttonPin, INPUT);
}
void loop() {
currentMillis = millis(); // one value for use throughout the loop
readButtons(); // checks for button presses
commandMotor(); // decides what mode to set the motor to
driveMotor(); // sends the instructions to the motor
}
void readButtons() {
if (digitalRead(FWDbuttonPin) == LOW) {
FWDbuttonstate = ! FWDbuttonstate; // this changes it to LOW if it was HIGH
// and to HIGH if it was LOW
}
if (digitalRead(REVbuttonPin) == LOW) {
REVbuttonstate = ! REVbuttonstate; // this changes it to LOW if it was HIGH
// and to HIGH if it was LOW
}
}
void commandMotor() {
if (FWDbuttonstate == HIGH) { // forward operation
if (currentMillis - previousButtonMillis <= FWD_Ramp_Interval) {
motorstate = motorSlowFWD;
previousButtonMillis += FWD_Ramp_Interval;
}
if (currentMillis - previousButtonMillis >= FWD_Ramp_Interval) {
motorstate = motorFastFWD;
}
REVbuttonstate = LOW; // reset reverse state
}
if (REVbuttonstate == HIGH) { // reverse operation
if (currentMillis - previousButtonMillis <= REV_Ramp_Interval) {
motorstate = motorSlowREV;
previousButtonMillis += REV_Ramp_Interval;
}
if (currentMillis - previousButtonMillis >= REV_Ramp_Interval) {
motorstate = motorFastREV;
}
FWDbuttonstate = LOW; // reset forward state
}
else { // neutral state
motorstate = motorOff;
}
}
void driveMotor() {
if (motorstate == motorOff) { SR.motor(0); }
if (motorstate == motorSlowFWD) { SR.motor(10); }
if (motorstate == motorFastFWD) { SR.motor(50); }
if (motorstate == motorSlowREV) { SR.motor(-10); }
if (motorstate == motorFastREV) { SR.motor(-50); }
}
Library link here (SyRen Simplified Serial is the relevant section)
Schematic attached.
The motor driver provides 5V in and requires only a serial connection through pin1. I hope this makes sense.
Using that library requires that your device be connected to the hardware serial pin. That means that, unless you have a Leonardo, you can no longer debug your sketch.
Given that there is little serial data that needs to be sent to the controller, the developer(s) of that library should be ashamed of themselves for not using SoftwareSerial and some pin(s) other than pin 1.
The library is incredibly simple. Delete it. Create an instance of SoftwareSerial that uses two other pins, and move the controller to the TX pin that you pass to the SoftwareSerial constructor.
Implement the one real useful method in the class in your sketch, and forget that those morons were trying to "help" you.
Erm....Ok, so I think I just about understand. I can use the standard SoftwareSerial Library to send data via a defined tx pin. When you say "useful method in the class in your sketch" I guess you mean the basic SR.motor command.
PaulS:
Aside from the constructors, the only other method is motor(). There is one useful version, and two useless ones.
Sorry, getting a little confused by the terminology. Is method() the same as member()? If I delete the library SyRenSimplified.h how do I send the member function motor() to the controller?
I chose the serial option because I thought it was simple. The Controller will accept a PWM signal instead if it is easier. I've amended the code to use pin 10 and 11 as RX and TX and send the motor () commands.
// Libraries
#include <SyRenSimplified.h>
#include <SoftwareSerial.h> // serial command library
SoftwareSerial SWSerial(10, 11);
SyRenSimplified SR(SWSerial);
// Constants
const int FWDbuttonPin = 2; // pin assignment
const int REVbuttonPin = 7;
const int txPin = 11;
const int rxPin = 10;
const int FWD_Ramp_Interval = 2500; // slow motor speed periods, allows independent values
const int REV_Ramp_Interval = 2500;
// Variables
byte FWDbuttonstate = LOW;
byte REVbuttonstate = LOW;
int motorOff;
int motorSlowFWD;
int motorFastFWD;
int motorSlowREV;
int motorFastREV;
int motorstate = motorOff;
unsigned long currentMillis = 0; // stores the value of millis() in each iteration of loop()
unsigned long previousButtonMillis = 0; // time when button press last checked
void setup() {
SWSerial.begin(9600);
pinMode(FWDbuttonPin, INPUT);
pinMode(REVbuttonPin, INPUT);
}
void loop() {
currentMillis = millis(); // one value for use throughout the loop
readButtons(); // checks for button presses
commandMotor(); // decides what mode to set the motor to
driveMotor(); // sends the instructions to the motor
}
void readButtons() {
if (digitalRead(FWDbuttonPin) == LOW) {
FWDbuttonstate = ! FWDbuttonstate; // this changes it to LOW if it was HIGH
// and to HIGH if it was LOW
}
if (digitalRead(REVbuttonPin) == LOW) {
REVbuttonstate = ! REVbuttonstate; // this changes it to LOW if it was HIGH
// and to HIGH if it was LOW
}
}
void commandMotor() {
if (FWDbuttonstate == HIGH) { // forward operation
if (currentMillis - previousButtonMillis <= FWD_Ramp_Interval) {
motorstate = motorSlowFWD;
previousButtonMillis += FWD_Ramp_Interval;
}
if (currentMillis - previousButtonMillis >= FWD_Ramp_Interval) {
motorstate = motorFastFWD;
}
REVbuttonstate = LOW; // reset reverse state
}
if (REVbuttonstate == HIGH) { // reverse operation
if (currentMillis - previousButtonMillis <= REV_Ramp_Interval) {
motorstate = motorSlowREV;
previousButtonMillis += REV_Ramp_Interval;
}
if (currentMillis - previousButtonMillis >= REV_Ramp_Interval) {
motorstate = motorFastREV;
}
FWDbuttonstate = LOW; // reset forward state
}
else { // neutral state
motorstate = motorOff;
}
}
void driveMotor() {
if (motorstate == motorOff) { SR.motor(0); }
if (motorstate == motorSlowFWD) {SR.motor(10); }
if (motorstate == motorFastFWD) { SR.motor(50); }
if (motorstate == motorSlowREV) { SR.motor(-10); }
if (motorstate == motorFastREV) { SR.motor(-50); }
}
SO I feel I'm getting somewhere but just can't get millis to delay as I want, tearing my hair out.
I've stripped back the motorCommand() function to just delay 5s before starting in each direction, I'll add the initial slower speed when I've got the basics right.
The motor starts, stops and changes direction as it should but the delay is not working. If anyone has any patience left with me please help.
// Libraries
#include <SoftwareSerial.h> // serial command library
SoftwareSerial SWSerial(10, 11);
// Constants
const int FWDbuttonPin = 2; // pin assignment
const int REVbuttonPin = 7;
const int txPin = 11;
const int rxPin = 10;
const int FWDled = 12;
const int REVled = 13;
const int FWD_Ramp_Interval = 5500; // slow motor speed periods, allows independent values
const int REV_Ramp_Interval = 5500;
long debounceDelay = 50;
// Variables
int FWDbuttonState = LOW;
int FWDlastButtonState = LOW;
int REVbuttonState = LOW;
int REVlastButtonState = LOW;
int FWDoutState = LOW; // LOW = off
int REVoutState = LOW;
unsigned long currentMillis = 0; // stores the value of millis() in each iteration of loop()
unsigned long previousFWDButtonMillis = 0;
unsigned long previousREVButtonMillis = 0;
long lastDebounceTime = 0;
void setup() {
SWSerial.begin(9600);
pinMode(FWDbuttonPin, INPUT);
pinMode(FWDled, OUTPUT);
pinMode(REVbuttonPin, INPUT);
pinMode(REVled, OUTPUT);
}
void loop() {
currentMillis = millis(); // one value for use throughout the loop
readButtons(); // checks for button presses
switchLeds();
motorCommand();
}
void readButtons() {
int FWDreading = digitalRead(FWDbuttonPin);
if (FWDreading != FWDlastButtonState) {
lastDebounceTime = currentMillis;
}
if ((currentMillis - lastDebounceTime) > debounceDelay) {
if (FWDreading != FWDbuttonState) {
FWDbuttonState = FWDreading;
if (FWDbuttonState == HIGH) {
FWDoutState = !FWDoutState;
}
}
}
if (FWDoutState == HIGH){
REVoutState = LOW;//Make sure reverse signal is cancelled
}
FWDlastButtonState = FWDreading;
int REVreading = digitalRead(REVbuttonPin);
if (REVreading != REVlastButtonState) {
lastDebounceTime = currentMillis;
}
if ((currentMillis - lastDebounceTime) > debounceDelay) {
if (REVreading != REVbuttonState) {
REVbuttonState = REVreading;
if (REVbuttonState == HIGH) {
REVoutState = !REVoutState;
}
}
}
if (REVoutState == HIGH){
FWDoutState = LOW;//Make sure reverse signal is cancelled
}
REVlastButtonState = REVreading;
}
void switchLeds() {
digitalWrite(FWDled, FWDoutState);
digitalWrite(REVled, REVoutState);
}
void motorCommand() {
if (FWDoutState == HIGH) {
if ((currentMillis - previousFWDButtonMillis) >= FWD_Ramp_Interval) {
{SWSerial.write(137); // wait 5 seconds then go fwd
previousFWDButtonMillis += FWD_Ramp_Interval;
}
}
}
if (REVoutState == HIGH) {
if ((currentMillis - previousREVButtonMillis) >= REV_Ramp_Interval) {
{SWSerial.write(117); //wait 5 seconds then go rev
previousREVButtonMillis += REV_Ramp_Interval;
}
}
}
else {
SWSerial.write(127); }
}