I'm somewhat of a newbie and I have been trying to convert my code from a sketch using blocking code with the Arduino stepper library to the accelstepper library. So far I have been unsuccessful and have tried many different iterations with different results most of the time.
My devices are as follows:
- Arduino Mega 2560 w/ built-in ESP8266
- 8BYJ-48 Stepper Motor w/ ULN2003 Motor Driver
- 2.4" TFT LCD Screen
I am making a wirelessly controlled watch winder that has a preset function, and a custom function that controls the number of rotations and delay between them. These functions can be controlled via the touch screen or a custom Blynk Android application.
Code using Arduino stepper library (blocking):
This code has been tested and the motor functions correctly from the testing I have done on it so far. The menu is slightly more complex as well.
//LCD Touch Screen
//Include the LCD Screen Libraries
#include "SPFD5408_Adafruit_GFX.h" // Core graphics library
#include "SPFD5408_Adafruit_TFTLCD.h" // Hardware-specific library
#include "SPFD5408_TouchScreen.h"
//Pin Assignments
#define YP A3
#define XM A2
#define YM 9
#define XP 8
//Calibrated Touch Values
#define TS_MINX 87
#define TS_MINY 108
#define TS_MAXX 956
#define TS_MAXY 964
//Touch Screen Initialization
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);
#define LCD_RD A0
#define LCD_WR A1
#define LCD_CD A2
#define LCD_CS A3
#define LCD_RESET A4
#define BOXSIZE 40
#define PENRADIUS 3
#define MINPRESSURE 10
#define MAXPRESSURE 1000
#define LED_PIN 13
//Initialize TFT Object
Adafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);
// Assign human-readable names to some common 16-bit color values:
#define BLACK 0x0000
#define BLUE 0x001F
#define RED 0xF800
#define GREEN 0x07E0
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
#define WHITE 0xFFFF
//Touch Screen Variables
int currentPage = 0; //variable to keep track of which menu page is to be displayed
bool touchLatch1 = false; //latch for default function loop
bool touchLatch2 = false; //latch for custom function loop
//Include the Blynk Library
#define BLYNK_PRINT Serial
#include <ESP8266_Lib.h>
#include <BlynkSimpleShieldEsp8266.h>
//Blynk Initialization
char auth[] = "khKRhoan-PFv7ZoQRdRf48DRb-HxAriL";
char ssid[] = "BELL300";
char pass[] = "4FA9F4A7";
#define EspSerial Serial3
#define ESP8266_BAUD 115200
ESP8266 wifi(&EspSerial);
//Blynk Variables
int switchStatus1; //start button for default function
bool latch1 = false; //latch for default function loop
int switchStatus2; //start button for custom function
bool latch2 = false; //latch for custom function loop
unsigned long rotationInput; //variable for custom function # of rotations
unsigned long pauseDelay; //variable for custom function, determines the pause duration at the end of the rotation cycle before repeating the cycle again
BlynkTimer timer; //define timer for function delays
//Include the Arduino Stepper Library
#include <Stepper.h>
//Motor Variables
// Number of steps per internal motor revolution
const float STEPS_PER_REV = 32;
int StepsRequired; // number of Steps Required
// The pins used are 22,23,24,25
// Connected to ULN2003 Motor Driver In1, In2, In3, In4
// Pins entered in sequence 1-3-2-4 for proper step sequencing
Stepper steppermotor(STEPS_PER_REV, 22, 24, 23, 25);
void setup()
{
Serial.begin(115200);
delay(10);
EspSerial.begin(ESP8266_BAUD); //serial initialization and default baud rate for ESP module
delay(10);
Blynk.begin(auth, wifi, ssid, pass); //Reguler server
//Blynk.begin(auth, wifi, ssid, pass,"blynk-cloud.com", 8080); //Local server
//Blynk.begin(auth, ssid, pass, IPAddress(45,55,96,146), 8080);
tft.reset();
tft.begin(0x9341); //start LCD screen
tft.setRotation(2); // set rotation to portrait
drawHome();
}
//Blynk Functions
//Default Function
// write data from blynk on virtual 1 (start default function)
BLYNK_WRITE(V1) {
switchStatus1 = param.asInt();
if (switchStatus1 == 1){ //if switch is on set latch and begin function loop
latch1 = true;
defaultRun();
}
if(switchStatus1 == 0){ //if switch is off unlatch
latch1 = false;
//turn off all power to stepper motor phases to conserve power.
digitalWrite(22,LOW);
digitalWrite(23,LOW);
digitalWrite(24,LOW);
digitalWrite(25,LOW);
}
}
//Custom Function
// write data from blynk on virtual 2 (start custom function)
BLYNK_WRITE(V2) {
switchStatus2 = param.asInt();
if (switchStatus2 == 1){ //if switch is on set latch and begin function loop
latch2 = true;
customRun();
}
if(switchStatus2 == 0){ //if switch is off unlatch
latch2 = false;
//update slider values on app to 0
Blynk.virtualWrite(V3, 0);
Blynk.virtualWrite(V4, 0);
//turn off all power to stepper motor phases to conserve power.
digitalWrite(22,LOW);
digitalWrite(23,LOW);
digitalWrite(24,LOW);
digitalWrite(25,LOW);
}
}
// write data from blynk on virtual 3 (rotation input)
BLYNK_WRITE(V3) {
rotationInput = map(param.asInt(), 0, 30, 0, 61500); //change slider value from 0-30 on app, to 0-61500 (61500 = # steps per single rotation X 30 = 2050 X 30)
}
// write data from blynk on virtual 4 (delay input slider)
BLYNK_WRITE(V4) {
pauseDelay = map(param.asInt(), 0, 10, 0, 36000000); //change slider value from 0-30 on app, to 0-36000000 (36000000 = 10 hours)
}
void loop(){
timer.run(); //call timer
Serial.println(touchLatch1);
Blynk.run(); //begin blynk
// Get the touch points
TSPoint p = ts.getPoint();
// Restore the mode
pinMode(XM, OUTPUT);
pinMode(YP, OUTPUT);
if (p.z > MINPRESSURE && p.z < MAXPRESSURE) {
p.x = map(p.x, TS_MINX, TS_MAXX, 0, tft.width());
p.y = map(p.y, TS_MINY, TS_MAXY, 0, tft.height());
// when at home page, if button is clicked set the appropriate page variable and draw the menu for the selected function
if(currentPage == 0){
if (p.x > 20 && p.x < 200){//defaultRun function button
if(p.y > 100 && p.y < 140){
timer.setTimeout(1000L, [](){ //wait 1 second before drawing next menu
tft.setRotation(2); // set rotation to portrait
tft.fillRoundRect(20, 100, 200, 40, 8, GREEN);
tft.drawRoundRect(20, 100, 200, 40, 8, WHITE);
tft.fillRoundRect(20, 180, 200, 40, 8, RED);
tft.drawRoundRect(20, 180, 200, 40, 8, WHITE);
tft.setCursor(55, 20);
tft.setTextSize(2);
tft.setTextColor(WHITE);
tft.print("PRESET WIND");
tft.setTextSize(2);
tft.setTextColor(WHITE);
tft.setCursor(90, 113);
tft.print("START");
tft.setTextSize(2);
tft.setTextColor(WHITE);
tft.setCursor(95, 193);
tft.print("STOP");
currentPage = 1;
});
}
}
if (p.x > 20 && p.x < 200){ //customRun function button
if(p.y > 180 && p.y < 220){
timer.setTimeout(1000L, [](){ //wait 1 second before drawing next menu
tft.setRotation(2); // set rotation to portrait
tft.fillRoundRect(20, 50, 200, 40, 8, GREEN);
tft.drawRoundRect(20, 50, 200, 40, 8, WHITE);
tft.fillRoundRect(20, 110, 200, 40, 8, RED);
tft.drawRoundRect(20, 110, 200, 40, 8, WHITE);
tft.fillTriangle(180, 197, 180, 217, 200, 207, BLUE);
tft.drawTriangle(180, 197, 180, 217, 200, 207, WHITE);
tft.fillRect(105, 197, 24, 20, WHITE);
tft.fillTriangle(55, 197, 55, 217, 37, 207, BLUE);
tft.drawTriangle(55, 197, 55, 217, 37, 207, WHITE);
tft.fillRect(105, 197, 24, 20, WHITE);
tft.fillTriangle(180, 264, 180, 284, 200, 274, BLUE);
tft.drawTriangle(180, 264, 180, 284, 200, 274, WHITE);
tft.fillRect(105, 264, 24, 20, WHITE);
tft.fillTriangle(55, 264, 55, 284, 37, 274, BLUE);
tft.drawTriangle(55, 264, 55, 284, 37, 274, WHITE);
tft.fillRect(105, 264, 24, 20, WHITE);
tft.setCursor(30, 20);
tft.setTextSize(2);
tft.setTextColor(WHITE);
tft.print("CUSTOM FUNCTION");
tft.setTextSize(2);
tft.setTextColor(WHITE);
tft.setCursor(90, 63);
tft.print("START");
tft.setTextSize(2);
tft.setTextColor(WHITE);
tft.setCursor(95, 123);
tft.print("STOP");
tft.setTextSize(2);
tft.setTextColor(WHITE);
tft.setCursor(36, 173);
tft.print("# OF ROTATIONS");
tft.setTextSize(2);
tft.setTextColor(BLACK);
tft.setCursor(106, 200);
tft.print(rotationInput);
tft.setTextSize(2);
tft.setTextColor(WHITE);
tft.setCursor(45, 240);
tft.print("DELAY (HOURS)");
tft.setTextSize(2);
tft.setTextColor(BLACK);
tft.setCursor(106, 267);
tft.print(pauseDelay);
currentPage = 2;
});
}
}
}
if(currentPage == 1){
if (p.x > 0 && p.x < 120) { // Touch area for box 1
if (p.y > 0 && p.y < 120) {
touchLatch1 = true;
defaultRun();
}
}
// Touch area for box 2
if (p.x > 120 && p.x < 240) {
if (p.y > 0 && p.y < 120) {
touchLatch1 = false;
//turn off all power to stepper motor pins
digitalWrite(22,LOW);
digitalWrite(23,LOW);
digitalWrite(24,LOW);
digitalWrite(25,LOW);
}
}
}
if(currentPage == 2){
if (p.x > 0 && p.x < 120) { // Touch area for box 1
if (p.y > 0 && p.y < 120) {
touchLatch2 = true;
customRun();
}
}
// Touch area for box 2
if (p.x > 120 && p.x < 240) {
if (p.y > 0 && p.y < 120) {
touchLatch2 = false;
//turn off all power to stepper motor pins
digitalWrite(22,LOW);
digitalWrite(23,LOW);
digitalWrite(24,LOW);
digitalWrite(25,LOW);
}
}
}
}
}
void drawHome(){ //display home screen at startup
if(currentPage == 0){
tft.fillScreen(BLACK);
tft.drawRoundRect(0, 0, 240, 320, 8, WHITE); //Page border
tft.fillRoundRect(20, 100, 200, 40, 8, RED);
tft.drawRoundRect(20, 100, 200, 40, 8, WHITE); //default function button
tft.fillRoundRect(20, 180, 200, 40, 8, RED);
tft.drawRoundRect(20, 180, 200, 40, 8, WHITE); //custom function button
tft.setCursor(23, 20);
tft.setTextSize(2);
tft.setTextColor(WHITE);
tft.print("SELECT AN OPTION");
tft.setTextSize(2);
tft.setTextColor(WHITE);
tft.setCursor(55, 113);
tft.print("PRESET WIND");
tft.setTextSize(2);
tft.setTextColor(WHITE);
tft.setCursor(55, 193);
tft.print("CUSTOM WIND");
}
}
//Default Function
/*================================================*/
void defaultRun(){
if(latch1 == true || touchLatch1 == true){ //only run if latch is true
steppermotor.setSpeed(1000);//define speed of motor rotation
StepsRequired = 2050; //one rotation = 2050 steps aprox.
steppermotor.step(StepsRequired);
timer.setTimeout(2000L, defaultRun_Reverse); //wait 2 seconds, then run reverse code
}
}
void defaultRun_Reverse(){
if(latch1 == true || touchLatch1 == true){ //only run if latch is true
steppermotor.setSpeed(1000);
StepsRequired = - 2050; //rotate in opposite direction
steppermotor.step(StepsRequired);
//turn off all power to stepper motor phases to conserve power.
digitalWrite(22,LOW);
digitalWrite(23,LOW);
digitalWrite(24,LOW);
digitalWrite(25,LOW);
timer.setTimeout(5000L, defaultRun); //wait 5 seconds before calling defaultRun function and repeating loop
}
}
/*================================================*/
//Custom Function
/*================================================*/
void customRun(){
if(latch2 == true || touchLatch2 == true){
steppermotor.setSpeed(1000);
StepsRequired = rotationInput; //read rotation value from app slider widget
steppermotor.step(StepsRequired); //read delay value from app slider widget
timer.setTimeout(2000L, customRun_Reverse);
}
}
void customRun_Reverse(){
if(latch2 == true || touchLatch2 == true){
steppermotor.setSpeed(1000);
StepsRequired = -rotationInput; //rotate in opposite direction
steppermotor.step(StepsRequired);
//turn off all power to stepper motor phases to conserve power.
digitalWrite(22,LOW);
digitalWrite(23,LOW);
digitalWrite(24,LOW);
digitalWrite(25,LOW);
timer.setTimeout(pauseDelay, customRun); //wait 5 seconds before calling defaultRun function and repeating loop
}
}
/*================================================*/
Code using Accelstepper (non-blocking):
I have so far only coded the default preset function. This code runs the motor for one rotation CW but then when it is to rotate CCW it begins to stop and stutter, and I am unsure why.
It also runs at a 1/3 of the speed that the blocking code does. I believe that this is because the Blynk connection along with the stepper motor running at the same time is possibly too much for the Mega board. Not sure if someone could confirm this. If this is the case, what would be my options?
//Include the Stepper Library
#include <AccelStepper.h>
//Motor Variables
#define FULLSTEP 4
AccelStepper stepper(FULLSTEP, 22, 24, 23, 25);
//Include the LCD Screen Libraries
#include "SPFD5408_Adafruit_GFX.h" // Core graphics library
#include "SPFD5408_Adafruit_TFTLCD.h" // Hardware-specific library
#include "SPFD5408_TouchScreen.h"
//Pin Assignments
#define YP A3
#define XM A2
#define YM 9
#define XP 8
//Calibrated Touch Values
#define TS_MINX 87
#define TS_MINY 108
#define TS_MAXX 956
#define TS_MAXY 964
//Touch Screen Initialization
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);
#define LCD_RD A0
#define LCD_WR A1
#define LCD_CD A2
#define LCD_CS A3
#define LCD_RESET A4
#define BOXSIZE 40
#define PENRADIUS 3
#define MINPRESSURE 10
#define MAXPRESSURE 1000
//Initialize TFT Object
Adafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);
// Assign human-readable names to some common 16-bit color values:
#define BLACK 0x0000
#define BLUE 0x001F
#define RED 0xF800
#define GREEN 0x07E0
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
#define WHITE 0xFFFF
//Touch Screen Variables
bool touchLatch1 = false;
bool touchLatch2 = false;
//Include the Blynk Library
#define BLYNK_PRINT Serial
#include <ESP8266_Lib.h>
#include <BlynkSimpleShieldEsp8266.h>
//Blynk Initialization
char auth[] = "khKRhoan-PFv7ZoQRdRf48DRb-HxAriL";
char ssid[] = "BELL300";
char pass[] = "4FA9F4A7";
#define EspSerial Serial3
#define ESP8266_BAUD 115200
ESP8266 wifi(&EspSerial);
//Blynk Variables
int switchStatus1; //start button for default function
bool latch1 = false; //latch for default function loop
int switchStatus2; //start button for custom function
bool latch2 = false; //latch for custom function loop
unsigned long rotationInput; //variable for custom function # of rotations
unsigned long pauseDelay; //variable for custom function, determines the pause duration at the end of the rotation cycle before repeating the cycle again
BlynkTimer timer; //define timer for function delays
void setup(){
Serial.begin(115200);
delay(10);
EspSerial.begin(ESP8266_BAUD); //serial initialization and default baud rate for ESP module
delay(10);
Blynk.begin(auth, wifi, ssid, pass); //Reguler server
//Blynk.begin(auth, wifi, ssid, pass,"blynk-cloud.com", 8080); //Local server
//Blynk.begin(auth, ssid, pass, IPAddress(45,55,96,146), 8080);
tft.reset();
tft.begin(0x9341);
tft.setRotation(2); // set rotation to portrait
tft.setTextColor(WHITE);
tft.setTextSize(2);
tft.fillScreen(BLACK);
tft.fillRect(0, 0, 120, 120, GREEN);
tft.fillRect(120, 0, 120, 120, RED);
tft.setCursor(15, 45);
tft.println("ON");
tft.setCursor(128, 45);
tft.println("OFF");
tft.setTextColor(WHITE, BLACK);
tft.setTextSize(8);
stepper.setMaxSpeed(1000.0);
stepper.setAcceleration(200.0);
stepper.setSpeed(1000);
}
//Blynk Functions
//Default Function
// write data from blynk on virtual 1 (start default function)
BLYNK_WRITE(V1) {
switchStatus1 = param.asInt();
if (switchStatus1 == 1){ //if switch is on set latch and begin function loop
latch1 = true;
}
if(switchStatus1 == 0){ //if switch is off unlatch
latch1 = false;
stepper.stop();
//turn off all power to stepper motor phases to conserve power.
digitalWrite(22,LOW);
digitalWrite(23,LOW);
digitalWrite(24,LOW);
digitalWrite(25,LOW);
}
}
//Custom Function
// write data from blynk on virtual 2 (start custom function)
BLYNK_WRITE(V2) {
switchStatus2 = param.asInt();
if (switchStatus2 == 1){ //if switch is on set latch and begin function loop
latch2 = true;
}
if(switchStatus2 == 0){ //if switch is off unlatch
latch2 = false;
//turn off all power to stepper motor phases to conserve power.
digitalWrite(22,LOW);
digitalWrite(23,LOW);
digitalWrite(24,LOW);
digitalWrite(25,LOW);
//update slider values on app to 0
Blynk.virtualWrite(V3, 0);
Blynk.virtualWrite(V4, 0);
stepper.stop();
}
}
// write data from blynk on virtual 3 (rotation input)
BLYNK_WRITE(V3) {
rotationInput = map(param.asInt(), 0, 30, 0, 122880); //change slider value from 0-30 on app, to 0-122880 (# steps per single rotation X 30 = 4096 X 30 = 122880)
}
// write data from blynk on virtual 4 (delay input slider)
BLYNK_WRITE(V4) {
pauseDelay = map(param.asInt(), 0, 10, 0, 36000000); //change slider value from 0-30 on app, to 0-36000000 (36000000 = 10 hours)
}
void loop(){
timer.run(); //call timer
Blynk.run(); //begin blynk and maintain connection
// Get the touch points
TSPoint p = ts.getPoint();
// Restore the mode
pinMode(XM, OUTPUT);
pinMode(YP, OUTPUT);
if (p.z > MINPRESSURE && p.z < MAXPRESSURE) {
p.x = map(p.x, TS_MINX, TS_MAXX, 0, tft.width());
p.y = map(p.y, TS_MINY, TS_MAXY, 0, tft.height());
// Touch area for box 1
if (p.x > 0 && p.x < 120) {
if (p.y > 0 && p.y < 120) {
touchLatch1 = true;
}
}
// Touch area for box 2
if (p.x > 120 && p.x < 240) {
if (p.y > 0 && p.y < 120) {
touchLatch1 = false;
stepper.stop();
//turn off all power to stepper motor phases to conserve power.
digitalWrite(22,LOW);
digitalWrite(23,LOW);
digitalWrite(24,LOW);
digitalWrite(25,LOW);
}
}
}
//Default Function
/*================================================*/
if(latch1 == true || touchLatch1 == true){ //only run if latch is true
stepper.moveTo(2048);
if (stepper.distanceToGo() == 0) {
stepper.moveTo(-stepper.currentPosition());
}
}
stepper.run();
/*================================================*/
//Custom Function
/*================================================
if(latch2 == true || touchLatch2 == true){
stepper.moveTo(rotationInput); //read # of rotations from app slider widget
if (stepper.distanceToGo() == 0){
timer.setTimeout(2000L, [](){
stepper.moveTo(rotationInput);; //rotate in opposite direction
timer.setTimeout(pauseDelay, [](){ //read delay value from app slider widget and wait before reapting loop
});
});
}
}*/
}