Renamed SpanStep to StepperControl and moved into HoneSpan Extras

Also updated StepperMotorControl Example.
This commit is contained in:
Gregg 2023-05-30 05:05:51 -05:00
parent d7aca25ac9
commit 5e1d65f436
8 changed files with 616 additions and 51 deletions

View File

@ -34,7 +34,7 @@
// that Example first if new to HomeSpan since it is fully commented.
#include "HomeSpan.h"
#include "Stepper_TB6612.h" // include stepper motor control for a TB6612 driver chip
#include "Stepper_TB6612.h"
////////////////////////////////////
@ -63,7 +63,7 @@ struct DEV_WindowShade : Service::WindowCovering {
// Move motor to absolute position, assuming 200 steps per revolution and 20 revolutions for full up/travel travel.
// Specify that motor should BRAKE upon moving to desired position.
motor->moveTo(target->getNewVal()*20,5,SpanStep::BRAKE);
motor->moveTo(target->getNewVal()*20,5,Stepper_TB6612::BRAKE);
LOG1("Setting Shade Position=%d\n",target->getNewVal());
return(true);
}

View File

@ -0,0 +1,115 @@
/*********************************************************************************
* 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.
*
********************************************************************************/
#include "extras/StepperControl.h"
//////////////////////////
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);
}
}
};
//////////////////////////

View File

@ -0,0 +1,30 @@
/*********************************************************************************
* MIT License
*
* Copyright (c) 2020-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.
*
********************************************************************************/
#pragma once
#include "../src/extras/StepperControl.h"

View File

@ -52,7 +52,7 @@ LedC::LedC(uint8_t pin, uint16_t freq, boolean invert){
timerList[nTimer][nMode]->duty_resolution=(ledc_timer_bit_t)res;
if(ledc_timer_config(timerList[nTimer][nMode])!=0){
ESP_LOGW(PWM_TAG,"Frequency=%d Hz is out of allowed range ---",freq);
ESP_LOGE(PWM_TAG,"Frequency=%d Hz is out of allowed range ---",freq);
delete timerList[nTimer][nMode];
timerList[nTimer][nMode]=NULL;
return;
@ -83,7 +83,7 @@ LedC::LedC(uint8_t pin, uint16_t freq, boolean invert){
LedPin::LedPin(uint8_t pin, float level, uint16_t freq, boolean invert) : LedC(pin, freq, invert){
if(!channel){
ESP_LOGW(PWM_TAG,"Can't create LedPin(%d) - no open PWM channels and/or Timers",pin);
ESP_LOGE(PWM_TAG,"Can't create LedPin(%d) - no open PWM channels and/or Timers",pin);
return;
}
else
@ -224,7 +224,7 @@ void LedPin::HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ){
ServoPin::ServoPin(uint8_t pin, double initDegrees, uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees) : LedC(pin, 50){
if(!channel)
ESP_LOGW(PWM_TAG,"Can't create ServoPin(%d) - no open PWM channels and/or Timers",pin);
ESP_LOGE(PWM_TAG,"Can't create ServoPin(%d) - no open PWM channels and/or Timers",pin);
else
ESP_LOGI(PWM_TAG,"ServoPin=%d: mode=%d channel=%d, timer=%d, freq=%d Hz, resolution=%d bits",
channel->gpio_num,

View File

@ -36,7 +36,7 @@ RFControl::RFControl(uint8_t pin, boolean refClock, boolean installDriver){
#else
if(nChannels==RMT_CHANNEL_MAX){
#endif
ESP_LOGW(RFControl_TAG,"Can't create RFControl(%d) - no open channels",pin);
ESP_LOGE(RFControl_TAG,"Can't create RFControl(%d) - no open channels",pin);
return;
}
@ -139,17 +139,17 @@ void RFControl::enableCarrier(uint32_t freq, float duty){
uint32_t lowTime=period*(1.0-duty)+0.5;
if(highTime>0xFFFF || lowTime>0xFFFF){
ESP_LOGW(RFControl_TAG,"Can't enable carrier frequency=%d Hz for RF Control pin=%d, duty=%0.2f. Frequency is too low!",freq,config->gpio_num,duty);
ESP_LOGE(RFControl_TAG,"Can't enable carrier frequency=%d Hz for RF Control pin=%d, duty=%0.2f. Frequency is too low!",freq,config->gpio_num,duty);
return;
}
if(highTime==0){
ESP_LOGW(RFControl_TAG,"Can't enable carrier frequency=%d Hz for RF Control pin=%d, duty=%0.2f. Duty is too low or frequency is too high!",freq,config->gpio_num,duty);
ESP_LOGE(RFControl_TAG,"Can't enable carrier frequency=%d Hz for RF Control pin=%d, duty=%0.2f. Duty is too low or frequency is too high!",freq,config->gpio_num,duty);
return;
}
if(lowTime==0){
ESP_LOGW(RFControl_TAG,"Can't enable carrier frequency=%d Hz for RF Control pin=%d, duty=%0.2f. Duty is too high or frequency is too high!",freq,config->gpio_num,duty);
ESP_LOGE(RFControl_TAG,"Can't enable carrier frequency=%d Hz for RF Control pin=%d, duty=%0.2f. Duty is too high or frequency is too high!",freq,config->gpio_num,duty);
return;
}

View File

@ -0,0 +1,171 @@
/*********************************************************************************
* MIT License
*
* Copyright (c) 2023 Gregg E. Berman
*
* 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
* 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.
*
********************************************************************************/
#include "StepperControl.h"
//////////////////////////
StepperControl::StepperControl(uint32_t priority, uint32_t cpu){
upLinkQueue = xQueueCreate(1,sizeof(upLink_t));
downLinkQueue = xQueueCreate(1,sizeof(downLink_t));
xTaskCreateUniversal(motorTask, "motorTaskHandle", 8096, this, priority, &motorTaskHandle, cpu);
}
//////////////////////////
void StepperControl::setAccel(float accelSize, float accelSteps){
if(accelSize<0.0){
ESP_LOGE(TAG,"accelSize cannot be less than 0.0");
return;
}
if(accelSteps<1.0){
ESP_LOGE(TAG,"accelSteps cannot be less than 1.0");
return;
}
this->accelSize=accelSize;
this->accelSteps=accelSteps;
}
//////////////////////////
void StepperControl::move(int nSteps, uint32_t msDelay, endAction_t endAction){
if(msDelay==0){
ESP_LOGE(TAG,"msDelay must be greater than zero");
return;
}
upLink_t upLinkData = { .nSteps=nSteps, .msDelay=msDelay, .absoluteStep=false, endAction=endAction };
xQueueReceive(downLinkQueue,&downLinkData,0);
downLinkData.stepsRemaining=nSteps;
xQueueOverwrite(upLinkQueue,&upLinkData);
}
//////////////////////////
void StepperControl::moveTo(int nPosition, uint32_t msDelay, endAction_t endAction){
if(msDelay==0){
ESP_LOGE(TAG,"msDelay must be greater than zero");
return;
}
upLink_t upLinkData = { .nSteps=nPosition, .msDelay=msDelay, .absoluteStep=true, .endAction=endAction };
xQueueReceive(downLinkQueue,&downLinkData,0);
downLinkData.stepsRemaining=nPosition-downLinkData.position;
xQueueOverwrite(upLinkQueue,&upLinkData);
}
//////////////////////////
int StepperControl::stepsRemaining(){
xQueuePeek(downLinkQueue,&downLinkData,0);
return(downLinkData.stepsRemaining);
}
//////////////////////////
int StepperControl::position(){
xQueuePeek(downLinkQueue,&downLinkData,0);
return(downLinkData.position);
}
//////////////////////////
void StepperControl::brake(){
move(0,10,BRAKE);
while(stepsRemaining());
}
//////////////////////////
void StepperControl::disable(){
move(0,10,DISABLE);
while(stepsRemaining());
}
//////////////////////////
void StepperControl::enable(){
move(0,10);
while(stepsRemaining());
}//////////////////////////
void StepperControl::motorTask(void *args){
StepperControl *motor = (StepperControl *)args;
upLink_t upLinkData = { .nSteps=0, .msDelay=10, .absoluteStep=false, .endAction=NONE };
downLink_t downLinkData = { .stepsRemaining=0, .position=0 };
boolean running=false;
for(;;){
if(xQueueReceive(motor->upLinkQueue, &upLinkData, 0)){
if(upLinkData.absoluteStep)
upLinkData.nSteps-=downLinkData.position;
downLinkData.stepsRemaining=upLinkData.nSteps;
motor->onEnable();
running=true;
}
uint32_t msDelay=upLinkData.msDelay;
if(running==false){
vTaskDelay(msDelay);
continue;
}
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));
int dStep=0;
if(downLinkData.stepsRemaining>0){
dStep=-1;
motor->onStep(1);
} else if(downLinkData.stepsRemaining<0){
dStep=1;
motor->onStep(0);
} else {
if(upLinkData.endAction==DISABLE)
motor->onDisable();
else if(upLinkData.endAction==BRAKE)
motor->onBrake();
running=false;
}
xQueueOverwrite(motor->downLinkQueue,&downLinkData);
downLinkData.stepsRemaining+=dStep;
downLinkData.position-=dStep;
vTaskDelay(msDelay);
}
}
//////////////////////////

View File

@ -0,0 +1,96 @@
/*********************************************************************************
* MIT License
*
* Copyright (c) 2023 Gregg E. Berman
*
* 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
* 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 <Arduino.h>
[[maybe_unused]] static const char* TAG = "StepperControl";
//////////////////////////
class StepperControl {
public:
enum {
FULL_STEP_ONE_PHASE=0,
FULL_STEP_TWO_PHASE=1,
HALF_STEP=2,
QUARTER_STEP=4,
EIGHTH_STEP=8
};
enum endAction_t {
NONE,
DISABLE,
BRAKE
};
private:
struct upLink_t {
int nSteps;
uint32_t msDelay;
boolean absoluteStep;
endAction_t endAction;
};
struct downLink_t {
int stepsRemaining;
int position;
};
float accelSteps=1;
float accelSize=0;
downLink_t downLinkData = { .stepsRemaining=0, .position=0 };
TaskHandle_t motorTaskHandle;
QueueHandle_t upLinkQueue;
QueueHandle_t downLinkQueue;
virtual void onStep(boolean direction)=0;
virtual void onEnable(){};
virtual void onDisable(){};
virtual void onBrake(){};
static void motorTask(void *args);
public:
StepperControl(uint32_t priority=2, uint32_t cpu=0);
virtual void setStepType(int mode){};
void setAccel(float accelSize, float accelSteps);
void move(int nSteps, uint32_t msDelay, endAction_t endAction=NONE);
void moveTo(int nPosition, uint32_t msDelay, endAction_t endAction=NONE);
int position();
int stepsRemaining();
void enable();
void disable();
void brake();
};
//////////////////////////

View File

@ -28,57 +28,210 @@
// 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 "PwmPin.h"
#include "StepperControl.h"
//////////////////////////
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); // start the Serial interface
Serial.flush();
delay(1000); // wait for interface to flush
Serial.println("\n\nHomeSpan LED Fade Test\n");
LedPin red(14,0);
LedPin green(14,0);
LedPin blue(14,0);
LedPin blue2(14,0,1000);
LedPin blue3(14,0,2000);
LedPin blue4(14,0,3000);
LedPin blue5(14,0,60000);
LedPin blue6(14,0,5000);
LedPin blue7(14,0,6000);
LedPin blue8(14,0,7000);
LedPin blue9(14,0,8000);
LedPin blue10(14,0,8000);
LedPin blue11(14,0,8000);
LedPin blue12(14,0,8000);
LedPin blue13(14,0,8000);
LedPin blue14(14,0,5000);
LedPin blue15(14,0,5000);
LedPin blue16(14,0,15000);
int redLevel=0;
for(int i=100;i<=100;i+=10){
while(red.fadeStatus()==LedPin::FADING);
red.fade(i,1000,LedPin::PROPORTIONAL);
}
while(1);
while(1){
Serial.begin(115200);
delay(1000);
if(red.fade(redLevel,5000))
Serial.printf("Failed\n");
else{
Serial.printf("Success\n");
redLevel=100-redLevel;
}
Serial.printf("\nReady.\n\n");
}
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);
}
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());
}
//////////////////////////