Skip to content

Instantly share code, notes, and snippets.

@devyte
Last active August 28, 2020 03:17
Show Gist options
  • Save devyte/ed3fa167d6a3b4296d2e074bdc865074 to your computer and use it in GitHub Desktop.
Save devyte/ed3fa167d6a3b4296d2e074bdc865074 to your computer and use it in GitHub Desktop.
Multichannel Stepper speed api based on waveform generator
#include "stepperapi.h"
#include "Arduino.h"
#include "core_esp8266_waveform.h"
static bool
stepperStartCore(int pinPulse, uint32_t analogFreq)
{
if (analogFreq == 0)
return stopWaveform(pinPulse); //for frequency = 0, stop the pulse train
if (analogFreq > 40000)
analogFreq = 40000; //clamp to 40KHz at the high end
uint32_t analogPeriod = microsecondsToClockCycles(1000000UL) / analogFreq;
constexpr uint32_t analogScale = 1023; //set up in setup(), 255 default if not set up
uint32_t high = analogPeriod / (2 * analogScale);
uint32_t low = analogPeriod - high;
pinMode(pinPulse, OUTPUT);
if ((low == 0) || (high == 0))
return false; //error
return startWaveformClockCycles(pinPulse, high, low, 0); //return whether start succeeded
}
bool
stepperStart(int pinEn, int pinPulse, uint32_t analogFreq)
{
//enable stepper
pinMode(pinEn, OUTPUT);
digitalWrite(pinEn, HIGH);
return stepperStartCore(pinPulse, analogFreq);
}
bool
stepperStop(int pinEn, int pinPulse)
{
if(!stopWaveform(pinPulse))
return false;
//Disable stepper
pinMode(pinEn, OUTPUT);
digitalWrite(pinEn, LOW);
return true;
}
bool
stepperUpdateSpeed(int pinPulse, uint32_t newAnalogFreq)
{
//update directly, no need to stop (see analogWrite() function body)
return stepperStartCore(pinPulse, newAnalogFreq);
}
#ifndef __STEPPERAPI_H__
#define __STEPPERAPI_H__
#include <cstdint>
bool
stepperStart(int pinEn, int pinPulse, uint32_t analogFreq);
bool
stepperStop(int pinEn, int pinPulse);
bool
stepperUpdateSpeed(int pinPulse, uint32_t newAnalogFreq);
#endif
#ifndef __STEPPERCLASS_H__
#define __STEPPERCLASS_H__
#include "stepperapi.h"
class Stepper
{
private:
int pinEn;
int pinPulse;
bool running;
public:
Stepper(int pinEn, int pinPulse)
: pinEn(pinEn),
pinPulse(pinPulse),
running(false)
{}
//disables the pins
~Stepper()
{
if(running)
{
stepperStop(pinEn, pinPulse);
releasePins();
}
}
//Enables the pins for OUTPUT
bool start(uint32_t speed)
{
if(stepperStart(pinEn, pinPulse, speed))
{
running = true;
return true;
}
return false;
}
//disables the stepper (En=false)
//Does not release the pins, i.e.: they remain in OUTPUT mode
bool stop()
{
if(stepperStop(pinEn, pinPulse)
{
running = false;
return true;
}
return false;
}
//speed=0 stops the pulse without doing EN=false
bool updateSpeed(uint32_t speed)
{
return stepperUpdateSpeed(pinPulse, speed);
}
//Disables the pins, i.e. they go back to INPUT mode and therefore float
void releasePins()
{
pinMode(pinEn, INPUT);
pinMode(pinPulse, INPUT);
}
bool isRunning()
{
return running;
}
};
#endif
#include "Arduino.h"
#include "stepperclass.h"
namespace
{
constexpr int pinEnS1 = 5;
constexpr int pinPulseS1 = 4;
constexpr int pinEnS2 = 0;
constexpr int pinPulseS2 = 2;
constexpr int pinEnS3 = 16;
constexpr int pinPulseS3 = 13;
constexpr int pinAlarm = 12;
constexpr int pinLinkageSensor = 14;
constexpr int pinRelay = 15;
Stepper stepper1(pinEnS1, pinPulseS1);
Stepper stepper2(pinEnS2, pinPulseS2);
Stepper stepper3(pinEnS3, pinPulseS3);
bool startFlag1 = false;
bool startFlag2 = false;
bool startFlag3 = false;
bool stopFlag1 = false;
bool stopFlag2 = false;
bool stopFlag3 = false;
bool updateFlag1 = false;
bool updateFlag2 = false;
bool updateFlag3 = false;
//The unit of these speeds is Hz, i.e.: pulses/second
uint32 speed1;
uint32 speed2;
uint32 speed3;
}
void
handleStepper(Stepper &stepper, bool &startFlag, bool &stopFlag, bool &updateFlag, uint32_t speed)
{
if(startFlag) //from ui
{
if(!stepper.start(speed))
{
//error starting
}
startFlag = false;
}
if(stopFlag) //from ui
{
if(!stepper.stop())
{
//error stopping
}
stopFlag = false;
}
if(updateFlag) //from ui
{
if(!stepper.updateSpeed(speed))
{
//error updating speed
}
updateFlag = false;
}
}
void
setup()
{
//set up wifi, webserver, etc
}
void
loop()
{
//startFlag, stopFlag, speed would be initially set from a webserver handler
//updateFlag and speed would then be set whenever a speed update is available,
//e.g.: gps gives new position => calculate speed set point in Hz (speedX = setPointVariableX / 10) => updateFlag = true
handleStepper(stepper1, startFlag1, stopFlag1, updateFlag1, speed1);
handleStepper(stepper2, startFlag2, stopFlag2, updateFlag2, speed2);
handleStepper(stepper3, startFlag3, stopFlag3, updateFlag3, speed3);
}
@moose4621
Copy link

For your info:
Pin allocations:
Rx = GPS Tx
A0 = Booster output voltage sensor.
GPIO0 = Stepper2 Enable
GPIO2 = Stepper2 Pulse
GPIO4 = Stepper1 Pulse
GPIO5 = Stepper1 Enable
GPIO12 = Alarm input
GPIO13 = Stepper3 Pulse
GPIO14 = Linkage sensor
GPIO15 = Relay output
GPIO16 = Stepper3 Enable
setPointVariable / 10 is the rpm required at any given time.

@moose4621
Copy link

{

if(startFlag) //from ui
{
uint32_t speed = setPointVariable / 10; //from ui
stepperStart(pinEn, pinPulse, speed);
startFlag = false;
}
if(stopFlag) //from ui
{
stepperStop(pinEn, linPulse);
stopFlag = false;
}
if(updateFlag) //from ui
{
uint32_t speed = setPointVariable / 10; //from ui
stepperUpdateSpeed(pinPulse, speed);
updateFlag = false;
}
}

@moose4621
Copy link

Looks easy enough so far.
Where does "startWaveformClockCycles" come from?

@devyte
Copy link
Author

devyte commented Aug 26, 2020

That's a core function that already exists

@devyte
Copy link
Author

devyte commented Aug 26, 2020

Ok, fleshed it out a bit now. I think you should be able to integrate with your current code. If you run into trouble, let me know.
BTW, the unit of speed above is Hz, you may need to convert from rpm or whatever you're using.
Pending: solve the low end speed < 100Hz

@moose4621
Copy link

For speed below 100Hz, can we count waves and only let every 10th wave through?

@devyte
Copy link
Author

devyte commented Aug 26, 2020

That's called a frequency divider, i.e.: you divide by some integer. Not with the current code. It might be one way to do it, but it has certain complexities

@devyte
Copy link
Author

devyte commented Aug 26, 2020

code is updated

@moose4621
Copy link

Latest errors.

Arduino: 1.8.12 (Linux), Board: "LOLIN(WEMOS) D1 R2 & mini, 80 MHz, Flash, Legacy (new can return nullptr), All SSL ciphers (most compatible), 4MB (FS:3MB OTA:~512KB), v2 Lower Memory, Disabled, None, Only Sketch, 921600"

/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:2:32: error: 'uint32_t' has not been declared
stepperStartCore(int pinPulse, uint32_t analogFreq)
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp: In function 'void stepperStartCore(int, int)':
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:11:3: error: 'uint32_t' was not declared in this scope
uint32_t analogPeriod = microsecondsToClockCycles(1000000UL) / analogFreq;
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:11:12: error: expected ';' before 'analogPeriod'
uint32_t analogPeriod = microsecondsToClockCycles(1000000UL) / analogFreq;
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:13:12: error: expected ';' before 'analogScale'
uint32_t analogScale = 1023; //set up in setup(), 255 default if not set up
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:15:12: error: expected ';' before 'high'
uint32_t high = (analogPeriod / 2) / analogScale;
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:16:12: error: expected ';' before 'low'
uint32_t low = analogPeriod - high;
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:17:21: error: 'OUTPUT' was not declared in this scope
pinMode(pinPulse, OUTPUT);
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:17:27: error: 'pinMode' was not declared in this scope
pinMode(pinPulse, OUTPUT);
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:18:7: error: 'low' was not declared in this scope
if (low == 0) {
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:21:7: error: 'high' was not declared in this scope
if (high == 0) {
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:24:42: error: 'high' was not declared in this scope
if (startWaveformClockCycles(pinPulse, high, low, 0)) {
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:24:48: error: 'low' was not declared in this scope
if (startWaveformClockCycles(pinPulse, high, low, 0)) {
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:24:54: error: 'startWaveformClockCycles' was not declared in this scope
if (startWaveformClockCycles(pinPulse, high, low, 0)) {
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp: At global scope:
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:30:39: error: 'uint32_t' has not been declared
stepperStart(int pinEn, int pinPulse, uint32_t analogFreq)
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp: In function 'void stepperStart(int, int, int)':
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:33:18: error: 'OUTPUT' was not declared in this scope
pinMode(pinEn, OUTPUT);
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:33:24: error: 'pinMode' was not declared in this scope
pinMode(pinEn, OUTPUT);
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:34:23: error: 'HIGH' was not declared in this scope
digitalWrite(pinEn, HIGH);
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:34:27: error: 'digitalWrite' was not declared in this scope
digitalWrite(pinEn, HIGH);
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:36:41: error: 'stepperStartPulse' was not declared in this scope
stepperStartPulse(pinPulse, analogFreq);
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp: In function 'void stepperStop(int, int)':
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:42:24: error: 'stopWaveForm' was not declared in this scope
stopWaveForm(pinPulse);
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:45:18: error: 'OUTPUT' was not declared in this scope
pinMode(pinEn, OUTPUT);
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:45:24: error: 'pinMode' was not declared in this scope
pinMode(pinEn, OUTPUT);
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:46:23: error: 'LOW' was not declared in this scope
digitalWrite(pinEn, LOW);
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:46:26: error: 'digitalWrite' was not declared in this scope
digitalWrite(pinEn, LOW);
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp: At global scope:
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:50:34: error: 'uint32_t' has not been declared
stepperUpdateSpeed(int pinPulse, uint32_t newAnalogFreq)
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp: In function 'void stepperUpdateSpeed(int, int)':
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:52:24: error: 'stopWaveForm' was not declared in this scope
stopWaveForm(pinPulse);
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:53:44: error: 'stepperStartPulse' was not declared in this scope
stepperStartPulse(pinPulse, newAnalogFreq);
^
exit status 1
Error compiling for board LOLIN(WEMOS) D1 R2 & mini.

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

@devyte
Copy link
Author

devyte commented Aug 26, 2020

updated

@moose4621
Copy link

Errors as of revision 14

Arduino: 1.8.12 (Linux), Board: "LOLIN(WEMOS) D1 R2 & mini, 80 MHz, Flash, Legacy (new can return nullptr), All SSL ciphers (most compatible), 4MB (FS:3MB OTA:~512KB), v2 Lower Memory, Disabled, None, Only Sketch, 921600"

/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp: In function 'void stepperStartCore(int, uint32_t)':
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:27:54: error: 'startWaveformClockCycles' was not declared in this scope
if (startWaveformClockCycles(pinPulse, high, low, 0)) {
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp: In function 'void stepperStart(int, int, uint32_t)':
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:39:41: error: 'stepperStartPulse' was not declared in this scope
stepperStartPulse(pinPulse, analogFreq);
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp: In function 'void stepperStop(int, int)':
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:45:24: error: 'stopWaveForm' was not declared in this scope
stopWaveForm(pinPulse);
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp: In function 'void stepperUpdateSpeed(int, uint32_t)':
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:55:24: error: 'stopWaveForm' was not declared in this scope
stopWaveForm(pinPulse);
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:56:44: error: 'stepperStartPulse' was not declared in this scope
stepperStartPulse(pinPulse, newAnalogFreq);
^
exit status 1
Error compiling for board LOLIN(WEMOS) D1 R2 & mini.

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

@devyte
Copy link
Author

devyte commented Aug 27, 2020

updated

@moose4621
Copy link

Added:
stepperapi.ccp line 1. removed space.
stepperapi.h line 7. added ;

then got these errors.
Arduino: 1.8.12 (Linux), Board: "LOLIN(WEMOS) D1 R2 & mini, 80 MHz, Flash, Legacy (new can return nullptr), All SSL ciphers (most compatible), 4MB (FS:3MB OTA:~512KB), v2 Lower Memory, Disabled, None, Only Sketch, 921600"

/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp: In function 'void stepperStop(int, int)':
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:46:24: error: 'stopWaveForm' was not declared in this scope
stopWaveForm(pinPulse);
^
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp: In function 'void stepperUpdateSpeed(int, uint32_t)':
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:56:24: error: 'stopWaveForm' was not declared in this scope
stopWaveForm(pinPulse);
^
exit status 1
Error compiling for board LOLIN(WEMOS) D1 R2 & mini.

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

@devyte
Copy link
Author

devyte commented Aug 27, 2020

fixed the two lines and the F

@moose4621
Copy link

Revision 18 compiles. Yay.

@devyte
Copy link
Author

devyte commented Aug 27, 2020

Updated:

  • to stop a stepper from moving, call stepper.updateSpeed(0)
  • to stop using a stepper, call stepper.stop(), this sets pinEn LOW
  • calling stepper.updateSpeed() should now have the pulse train change smoothly on the next low->high transition

@moose4621
Copy link

Found a coupkle of typo's but this one is out of my league.

Arduino: 1.8.12 (Linux), Board: "LOLIN(WEMOS) D1 R2 & mini, 80 MHz, Flash, Legacy (new can return nullptr), All SSL ciphers (most compatible), 4MB (FS:3MB OTA:~512KB), v2 Lower Memory, Disabled, None, Only Sketch, 921600"

/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp: In function 'bool stepperStartCore(int, uint32_t)':
/home/chris/Arduino/libraries/stepperapi/stepperapi.cpp:10:33: error: 'stopWaveForm' was not declared in this scope
return stopWaveForm(pinPulse); //for frequency = 0, stop the pulse train
^
exit status 1
Error compiling for board LOLIN(WEMOS) D1 R2 & mini.

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

@devyte
Copy link
Author

devyte commented Aug 27, 2020

also a typo: F->f
what are the others?

@moose4621
Copy link

line 43 in .ino needs another )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment