Added Stepper_TB6612 and Stepper_A3967 directly to HomeSpan

HomeSpan now includes both drivers as part of the "extras" functionality.
The StepperMotorControl Window Shade Example has been updated accordingly to use the built-in TB6612 driver.
This commit is contained in:
Gregg 2023-06-09 23:11:38 -05:00
parent 44e48a4534
commit 536de53075
8 changed files with 333 additions and 273 deletions

View File

@ -26,24 +26,19 @@
********************************************************************************/
// This example demonstrates how to control a real-world Stepper Motor using HomeSpan's
// StepperControl Class. Note the StepperControl Class serves as a generic interface
// and cannot be instantiatiated directly. Rather, you must create a child class derived
// from StepperClass that implements the details of a specific Stepper Motor Driver Board.
// generic StepperControl Class.
// In this example we will implement a child class of StepperControl designed to operate
// the Adafruit TB6612 1.2A DC/Stepper Motor Driver Breakout Board using only the 4 control
// pins AIN1, AIN2, BIN1, and BIN2 (https://www.adafruit.com/product/2448)
// For this sketch we will use an implementation of StepperControl designed to operate an
// Adafruit TB6612 1.2A DC/Stepper Motor Driver Breakout Board (https://www.adafruit.com/product/2448)
// using only its 4 control pins: AIN1, AIN2, BIN1, and BIN2
// The implementation of this class is found in the file "Stepper_TB6612.h" included
// with this sketch. By separating the details of the Stepper Motor Driver Board from the
// HomeSpan logic below, you can easily change the code that operates the Stepper Motor Driver Board
// without very little modification to the HomeSpan sketch itself.
// See HomeSpan's StepperControl documentation for a list of built-in driver boards, as well as for
// detailed instructions on how you can easily use StepperControl to implement a custom driver for any board.
// Note this sketch is based on the WindowShade Accessory from Example 13. Please review
// that Example first if new to HomeSpan since it is fully commented.
// Note this example is based on the fully-commented WindowShade Accessory sketch from Tutorial Example 13.
#include "HomeSpan.h"
#include "Stepper_TB6612.h" // Here we include the implementation of thge Adafruit TB6612 Stepper Motor Driver Board
#include "extras/Stepper_TB6612.h" // this contains HomeSpan's StepperControl Class for the Adafruit TB6612 Driver Board
////////////////////////////////////

View File

@ -0,0 +1,30 @@
/*********************************************************************************
* MIT License
*
* Copyright (c) 2020-2022 Gregg E. Berman
*
* https://github.com/HomeSpan/HomeSpan
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
********************************************************************************/
#pragma once
#include "../src/extras/Stepper_A3967.h"

View File

@ -0,0 +1,30 @@
/*********************************************************************************
* MIT License
*
* Copyright (c) 2020-2022 Gregg E. Berman
*
* https://github.com/HomeSpan/HomeSpan
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
********************************************************************************/
#pragma once
#include "../src/extras/Stepper_TB6612.h"

View File

@ -40,12 +40,12 @@ StepperControl::StepperControl(uint32_t priority, uint32_t cpu){
void StepperControl::setAccel(float accelSize, float accelSteps){
if(accelSize<0.0){
ESP_LOGE(TAG,"accelSize cannot be less than 0.0");
ESP_LOGE(STEPPER_TAG,"accelSize cannot be less than 0.0");
return;
}
if(accelSteps<1.0){
ESP_LOGE(TAG,"accelSteps cannot be less than 1.0");
ESP_LOGE(STEPPER_TAG,"accelSteps cannot be less than 1.0");
return;
}
@ -57,7 +57,7 @@ void StepperControl::setAccel(float accelSize, float accelSteps){
void StepperControl::move(int nSteps, uint32_t msDelay, endAction_t endAction){
if(msDelay==0){
ESP_LOGE(TAG,"msDelay must be greater than zero");
ESP_LOGE(STEPPER_TAG,"msDelay must be greater than zero");
return;
}
@ -71,7 +71,7 @@ void StepperControl::move(int nSteps, uint32_t msDelay, endAction_t endAction){
void StepperControl::moveTo(int nPosition, uint32_t msDelay, endAction_t endAction){
if(msDelay==0){
ESP_LOGE(TAG,"msDelay must be greater than zero");
ESP_LOGE(STEPPER_TAG,"msDelay must be greater than zero");
return;
}
@ -143,7 +143,7 @@ void StepperControl::motorTask(void *args){
if(downLinkData.stepsRemaining!=0)
msDelay+=msDelay * motor->accelSize * (exp(-fabs(upLinkData.nSteps-downLinkData.stepsRemaining)/motor->accelSteps) + exp(-(fabs(downLinkData.stepsRemaining)-1.0)/motor->accelSteps));
ESP_LOGD(TAG,"Position: %d Steps Remaining: %d Delay=%d ms",downLinkData.position,downLinkData.stepsRemaining,(int)(msDelay));
ESP_LOGD(STEPPER_TAG,"Position: %d Steps Remaining: %d Delay=%d ms",downLinkData.position,downLinkData.stepsRemaining,(int)(msDelay));
int dStep=0;

View File

@ -29,7 +29,7 @@
#include <Arduino.h>
[[maybe_unused]] static const char* TAG = "StepperControl";
[[maybe_unused]] static const char* STEPPER_TAG = "StepperControl";
//////////////////////////

View File

@ -3,7 +3,7 @@
*
* Copyright (c) 2023 Gregg E. Berman
*
* https://github.com/HomeSpan/HomeSpan
* https://github.com/HomeSpan/HomeStep
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -25,88 +25,92 @@
*
********************************************************************************/
#include "extras/StepperControl.h"
// Implementation of StepperControl for a Sparkfun A3967 EasyDriver Stepper Motor Driver
// Breakout Board (https://www.sparkfun.com/products/12779)
// This implementation uses the driver board's MS1, MS2, STEP, DIR, and ENABLE pins.
// Separate PWM pins on the ESP32 are NOT needed since the driver board contains its own
// waveform generation. Supported modes include FULL_STEP (double-phase only), HALF_STEP,
// QUARTER_STEP, and EIGHTH_STEP.
// The motor outputs can be enabled (current running through the coils) or
// disabled (no current / high impedence). The EasyDriver board does NOT support
// the short brake mode.
#pragma once
#include "StepperControl.h"
//////////////////////////
struct Stepper_TB6612 : StepperControl {
struct Stepper_A3967 : StepperControl {
int pins[4];
uint8_t phase;
uint8_t nPhases;
uint32_t runCode;
int m1Pin;
int m2Pin;
int stepPin;
int dirPin;
int enablePin;
//////////////////////////
Stepper_TB6612(int a1Pin, int a2Pin, int b1Pin, int b2Pin) : StepperControl(){
pins[3]=a1Pin;
pins[2]=a2Pin;
pins[1]=b1Pin;
pins[0]=b2Pin;
Stepper_A3967(int m1Pin, int m2Pin, int stepPin, int dirPin, int enablePin) : StepperControl(){
this->m1Pin=m1Pin;
this->m2Pin=m2Pin;
this->stepPin=stepPin;
this->dirPin=dirPin;
this->enablePin=enablePin;
for(int i=0;i<4;i++)
pinMode(pins[i],OUTPUT);
pinMode(m1Pin,OUTPUT);
pinMode(m2Pin,OUTPUT);
pinMode(stepPin,OUTPUT);
pinMode(dirPin,OUTPUT);
pinMode(enablePin,OUTPUT);
setStepType(FULL_STEP_TWO_PHASE);
}
//////////////////////////
void onStep(boolean direction){
digitalWrite(dirPin,direction);
digitalWrite(stepPin,HIGH);
digitalWrite(stepPin,LOW);
}
//////////////////////////
void onEnable() override {
setPinCode((runCode>>(phase*4)) & 0xF);
digitalWrite(enablePin,0);
}
//////////////////////////
void onDisable() override {
setPinCode(0);
}
//////////////////////////
void onBrake() override {
setPinCode(0xF);
}
//////////////////////////
void onStep(boolean direction){
if(direction)
phase=(phase+1)%nPhases;
else
phase=(phase+nPhases-1)%nPhases;
setPinCode((runCode>>(phase*4)) & 0xF);
}
//////////////////////////
void setPinCode(uint8_t pinCode){
for(int i=0;i<4;i++)
digitalWrite(pins[i],(pinCode>>i)&1);
digitalWrite(enablePin,1);
}
//////////////////////////
void setStepType(int mode) override {
switch(mode){
case FULL_STEP_ONE_PHASE:
phase=0;
nPhases=4;
runCode=0x2418;
break;
case FULL_STEP_TWO_PHASE:
phase=0;
nPhases=4;
runCode=0x659A;
digitalWrite(m1Pin,LOW);
digitalWrite(m2Pin,LOW);
break;
case HALF_STEP:
phase=0;
nPhases=8;
runCode=0x2645198A;
digitalWrite(m1Pin,HIGH);
digitalWrite(m2Pin,LOW);
break;
case QUARTER_STEP:
digitalWrite(m1Pin,LOW);
digitalWrite(m2Pin,HIGH);
break;
case EIGHTH_STEP:
digitalWrite(m1Pin,HIGH);
digitalWrite(m2Pin,HIGH);
break;
default:
ESP_LOGE(TAG,"Unknown StepType=%d. Stepper Unchanged",mode);
ESP_LOGE(STEPPER_TAG,"Unknown StepType=%d",mode);
}
}

View File

@ -0,0 +1,181 @@
/*********************************************************************************
* MIT License
*
* Copyright (c) 2023 Gregg E. Berman
*
* https://github.com/HomeSpan/HomeSpan
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
********************************************************************************/
// Implementation of StepperControl for an Adafruit TB6612 1.2A DC/Stepper Motor Driver
// Breakout Board (https://www.adafruit.com/product/2448)
// This implementation supports two constructors reflecting implementations both with and
// without the use of PWM pins. The first operates the driver board using only its
// four digital control pins: AIN1, AIN2, BIN1, BIN2. In this configuration the
// PWMA, PWMB, and STBY pins on the driver board should be directly connected to Vcc.
// The second configuration uses the four digital control pins (AIN1, AIN2, BIN1, and BIN2)
// as well as the PWMA and PWMB pins. In this configuration only the STBY pin on the
// driver board should be directly connected to Vcc.
// The first configuration supports both single-phase and double-phase FULL_STEP modes,
// as well as a HALF_STEP mode. The second configuration also includes QUARTER_STEP
// and EIGHTH_STEP modes, made possible by the use of the PWM pins to micro-step the motor.
// In either configuration the motor outputs can be enabled (current running through the coils)
// disabled (no current / high impedence) or set to a short brake.
#pragma once
#include "StepperControl.h"
#include "PwmPin.h"
//////////////////////////
struct Stepper_TB6612 : StepperControl {
int ain1, ain2, bin1, bin2;
uint8_t phase, nPhases;
double offset;
LedPin *pwmA=NULL, *pwmB;
//////////////////////////
Stepper_TB6612(int AIN1, int AIN2, int BIN1, int BIN2) : StepperControl(){
ain1=AIN1;
ain2=AIN2;
bin1=BIN1;
bin2=BIN2;
pinMode(ain1,OUTPUT);
pinMode(ain2,OUTPUT);
pinMode(bin1,OUTPUT);
pinMode(bin2,OUTPUT);
setStepType(FULL_STEP_TWO_PHASE);
}
//////////////////////////
Stepper_TB6612(int AIN1, int AIN2, int BIN1, int BIN2, int PWMA, int PWMB) : Stepper_TB6612(AIN1,AIN2,BIN1,BIN2){
pwmA=new LedPin(PWMA,0,50000);
pwmB=new LedPin(PWMB,0,50000);
}
//////////////////////////
void onEnable() override {
setPins();
}
//////////////////////////
void onDisable() override {
digitalWrite(ain1,0);
digitalWrite(ain2,0);
digitalWrite(bin1,0);
digitalWrite(bin2,0);
}
//////////////////////////
void onBrake() override {
digitalWrite(ain1,1);
digitalWrite(ain2,1);
digitalWrite(bin1,1);
digitalWrite(bin2,1);
}
//////////////////////////
void onStep(boolean direction){
if(direction)
phase=(phase+1)%nPhases;
else
phase=(phase+nPhases-1)%nPhases;
setPins();
}
//////////////////////////
void setPins(){
float levelA=cos(phase*TWO_PI/nPhases+offset)*100.0;
float levelB=sin(phase*TWO_PI/nPhases+offset)*100.0;
digitalWrite(ain1,levelA>0.01);
digitalWrite(ain2,levelA<-0.01);
digitalWrite(bin1,levelB>0.01);
digitalWrite(bin2,levelB<-0.01);
if(pwmA){
pwmA->set(fabs(levelA));
pwmB->set(fabs(levelB));
}
}
//////////////////////////
void setStepType(int mode) override {
switch(mode){
case FULL_STEP_ONE_PHASE:
phase=0;
nPhases=4;
offset=0;
break;
case FULL_STEP_TWO_PHASE:
phase=0;
nPhases=4;
offset=TWO_PI/8.0;
break;
case HALF_STEP:
phase=0;
nPhases=8;
offset=0;
break;
case QUARTER_STEP:
if(!pwmA){
ESP_LOGE(STEPPER_TAG,"QUARTER_STEP requires PWM pins");
return;
}
phase=0;
nPhases=16;
offset=0;
break;
case EIGHTH_STEP:
if(!pwmA){
ESP_LOGE(STEPPER_TAG,"EIGHTH_STEP requires PWM pins");
return;
}
phase=0;
nPhases=32;
offset=0;
break;
default:
ESP_LOGE(STEPPER_TAG,"Unknown StepType=%d",mode);
}
}
};
//////////////////////////

View File

@ -28,215 +28,35 @@
// This is a placeholder .ino file that allows you to easily edit the contents of this files using the Arduino IDE,
// as well as compile and test from this point. This file is ignored when the library is included in other sketches.
#include "StepperControl.h"
#include "Stepper_TB6612.h"
#include "Stepper_A3967.h"
//////////////////////////
StepperControl *motor;
struct Stepper_TB6612 : StepperControl {
int pins[4];
uint8_t phase;
uint8_t nPhases;
uint32_t runCode;
//////////////////////////
Stepper_TB6612(int a1Pin, int a2Pin, int b1Pin, int b2Pin) : StepperControl(){
pins[3]=a1Pin;
pins[2]=a2Pin;
pins[1]=b1Pin;
pins[0]=b2Pin;
for(int i=0;i<4;i++)
pinMode(pins[i],OUTPUT);
setStepType(FULL_STEP_TWO_PHASE);
}
//////////////////////////
void onEnable() override {
setPinCode((runCode>>(phase*4)) & 0xF);
}
//////////////////////////
void onDisable() override {
setPinCode(0);
}
//////////////////////////
void onBrake() override {
setPinCode(0xF);
}
//////////////////////////
void onStep(boolean direction){
if(direction)
phase=(phase+1)%nPhases;
else
phase=(phase+nPhases-1)%nPhases;
setPinCode((runCode>>(phase*4)) & 0xF);
}
//////////////////////////
void setPinCode(uint8_t pinCode){
for(int i=0;i<4;i++)
digitalWrite(pins[i],(pinCode>>i)&1);
}
//////////////////////////
void setStepType(int mode) override {
switch(mode){
case FULL_STEP_ONE_PHASE:
phase=0;
nPhases=4;
runCode=0x2418;
break;
case FULL_STEP_TWO_PHASE:
phase=0;
nPhases=4;
runCode=0x659A;
break;
case HALF_STEP:
phase=0;
nPhases=8;
runCode=0x2645198A;
break;
default:
ESP_LOGE(TAG,"Unknown StepType=%d. Stepper Unchanged",mode);
}
}
};
//////////////////////////
struct Stepper_A3967 : StepperControl {
int m1Pin;
int m2Pin;
int stepPin;
int dirPin;
int enablePin;
//////////////////////////
Stepper_A3967(int m1Pin, int m2Pin, int stepPin, int dirPin, int enablePin) : StepperControl(){
this->m1Pin=m1Pin;
this->m2Pin=m2Pin;
this->stepPin=stepPin;
this->dirPin=dirPin;
this->enablePin=enablePin;
pinMode(m1Pin,OUTPUT);
pinMode(m2Pin,OUTPUT);
pinMode(stepPin,OUTPUT);
pinMode(dirPin,OUTPUT);
pinMode(enablePin,OUTPUT);
setStepType(FULL_STEP_TWO_PHASE);
}
//////////////////////////
void onStep(boolean direction){
digitalWrite(dirPin,direction);
digitalWrite(stepPin,HIGH);
digitalWrite(stepPin,LOW);
}
//////////////////////////
void onEnable() override {
digitalWrite(enablePin,0);
}
//////////////////////////
void onDisable() override {
digitalWrite(enablePin,1);
}
//////////////////////////
void setStepType(int mode) override {
switch(mode){
case FULL_STEP_TWO_PHASE:
digitalWrite(m1Pin,LOW);
digitalWrite(m2Pin,LOW);
break;
case HALF_STEP:
digitalWrite(m1Pin,HIGH);
digitalWrite(m2Pin,LOW);
break;
case QUARTER_STEP:
digitalWrite(m1Pin,LOW);
digitalWrite(m2Pin,HIGH);
break;
case EIGHTH_STEP:
digitalWrite(m1Pin,HIGH);
digitalWrite(m2Pin,HIGH);
break;
default:
ESP_LOGE(TAG,"Unknown StepType=%d. Stepper Unchanged",mode);
}
}
};
//////////////////////////
StepperControl *motor[2];
////////////////////////////////////
void setup() {
Serial.begin(115200);
delay(1000);
Serial.printf("\nReady.\n\n");
Serial.printf("\nHomeSpan Steppers\n\n");
pinMode(33,OUTPUT);
pinMode(27,OUTPUT);
digitalWrite(33,HIGH);
digitalWrite(27,HIGH);
motor[0]=new Stepper_A3967(16,17,21,19,18);
motor[1]=new Stepper_TB6612(23,32,22,14);
motor[0]->setStepType(Stepper_A3967::QUARTER_STEP);
motor[1]->setStepType(Stepper_TB6612::HALF_STEP);
motor[0]->setAccel(10,20);
motor[1]->setAccel(50,100);
motor=new Stepper_TB6612(23,32,22,14,33,27);
// motor=new Stepper_TB6612(23,32,22,14);
motor->setStepType(StepperControl::EIGHTH_STEP);
motor->setAccel(10,100);
motor->move(1600,2);
while(motor->stepsRemaining());
motor->moveTo(0,2,StepperControl::BRAKE);
}
//////////////////////////////////////
void loop(){
motor[0]->move(1000,5);
motor[1]->move(2000,2);
do {
for(int i=0;i<2;i++)
Serial.printf("Motor %d - %5d %5d ",i,motor[i]->stepsRemaining(),motor[i]->position());
Serial.printf("\n");
delay(500);
} while(motor[0]->stepsRemaining() || motor[1]->stepsRemaining());
motor[0]->moveTo(0,5);
motor[1]->moveTo(0,2);
do {
for(int i=0;i<2;i++)
Serial.printf("Motor %d - %5d %5d ",i,motor[i]->stepsRemaining(),motor[i]->position());
Serial.printf("\n");
delay(500);
} while(motor[0]->stepsRemaining() || motor[1]->stepsRemaining());
}
//////////////////////////////////////
//////////////////////////