199 lines
6.8 KiB
C++
199 lines
6.8 KiB
C++
//******************************************************************************************
|
|
// File: EX_Servo.cpp
|
|
// Authors: Dan G Ogorchock
|
|
//
|
|
// Summary: EX_Servo is a class which implements the SmartThings/Hubitat "Switch Level" device capability.
|
|
// It inherits from the st::Executor class.
|
|
//
|
|
// Create an instance of this class in your sketch's global variable section
|
|
// For Example: st::EX_Servo executor1(F("servo1"), PIN_SERVO, 90, true, 1000, 0, 180, 2000, 544, 2400);
|
|
//
|
|
// st::EX_Servo() constructor requires the following arguments
|
|
// - String &name - REQUIRED - the name of the object - must match the Groovy ST_Anything DeviceType tile name
|
|
// - byte pin_pwm - REQUIRED - the Arduino Pin to be used as a pwm output
|
|
// - int startingAngle - OPTIONAL - the value desired for the initial angle of the servo motor (0 to 180, defaults to 90)
|
|
// - bool detachAfterMove - OPTIONAL - determines if servo motor is powered down after move (defaults to false)
|
|
// - int servoDetachTime - OPTIONAL - determines how long after the servo is moved that the servo is powered down if detachAfterMove is true (defaults to 1000ms)
|
|
// - int minLevelAngle - OPTIONAL - servo angle in degrees to map to level 0 (defaults to 0 degrees)
|
|
// - int maxLevelAngle - OPTIONAL - servo angle in degrees to map to level 100 (defaults to 180 degrees)
|
|
// - int servoRate - OPTIONAL - initial servo rate in ms/degree (defaults to 2000, used to ensure a gentle move during startup, afterwards comes from SmartThings/Hubitat with each move request)
|
|
// - int minPulseWidth - OPTIONAL - minimum pulse width in milliseconds, defaults to 544 (see Arduino servo attach() function)
|
|
// - int maxPulseWidth - OPTIONAL - maximum pulse width in milliseconds, defaults to 2400 (see Arduino servo attach() function)
|
|
//
|
|
// Change History:
|
|
//
|
|
// Date Who What
|
|
// ---- --- ----
|
|
// 2018-06-23 Dan Ogorchock Original Creation
|
|
// 2018-06-24 Dan Ogorchock Since ESP32 does not support SERVO library, exclude all code to prevent compiler error
|
|
// 2018-08-19 Dan Ogorchock Added feature to optionally allow servo to be powered down after a move
|
|
// 2019-02-02 Jeff Albers Added Parameters to map servo endpoints, actively control rate of servo motion via duration input to device driver, intializes to level instead of angle
|
|
// 2019-02-09 Dan Ogorchock Adding Asynchronous Motion to eliminate blocking calls and to allow simultaneous motion across multiple servos
|
|
// 2019-03-04 Dan Ogorchock Added optional min and max pulse width parameters to allow servo specific adjustments
|
|
//
|
|
//
|
|
//******************************************************************************************
|
|
#include "EX_Servo.h"
|
|
|
|
#include "Constants.h"
|
|
#include "Everything.h"
|
|
|
|
#if not defined(ARDUINO_ARCH_ESP32)
|
|
|
|
namespace st
|
|
{
|
|
//private
|
|
void EX_Servo::writeAngleToPin()
|
|
{
|
|
if(m_bMoveActive == true) {
|
|
m_bMoveActive = false;
|
|
}
|
|
|
|
if (!m_Servo.attached()) {
|
|
m_Servo.attach(m_nPinPWM, m_nMinPulseWidth, m_nMaxPulseWidth);
|
|
}
|
|
|
|
if ((m_nTargetAngle < m_nMinLevelAngle) and (m_nTargetAngle < m_nMaxLevelAngle)) {
|
|
if (m_nMinLevelAngle < m_nMaxLevelAngle) {
|
|
m_nTargetAngle = m_nMinLevelAngle;
|
|
}
|
|
else {
|
|
m_nTargetAngle = m_nMaxLevelAngle;
|
|
}
|
|
}
|
|
if ((m_nTargetAngle > m_nMaxLevelAngle) and (m_nTargetAngle > m_nMinLevelAngle)) {
|
|
if (m_nMaxLevelAngle > m_nMinLevelAngle) {
|
|
m_nTargetAngle = m_nMaxLevelAngle;
|
|
}
|
|
else {
|
|
m_nTargetAngle = m_nMinLevelAngle;
|
|
}
|
|
}
|
|
|
|
m_nTimeStep = abs(m_nCurrentRate / (m_nMaxLevelAngle - m_nMinLevelAngle)); //Constant servo step rate assumes duration is the time desired for maximum level change of 100
|
|
m_nCurrentAngle = m_nOldAngle; //preserver original angular position
|
|
m_bMoveActive = true; //start the move (the update() function will take care of the actual motion)
|
|
|
|
if (st::Executor::debug) {
|
|
Serial.print(F("EX_Servo:: Servo motor angle set to "));
|
|
Serial.println(m_nTargetAngle);
|
|
}
|
|
|
|
}
|
|
|
|
//public
|
|
//constructor
|
|
EX_Servo::EX_Servo(const __FlashStringHelper *name, byte pinPWM, int startingAngle, bool detachAfterMove, long servoDetachTime, int minLevelAngle, int maxLevelAngle, int servoRate, int minPulseWidth, int maxPulseWidth) :
|
|
Executor(name),
|
|
m_Servo(),
|
|
m_nTargetAngle(startingAngle),
|
|
m_bDetachAfterMove(detachAfterMove),
|
|
m_nDetachTime(servoDetachTime),
|
|
m_nMinLevelAngle(minLevelAngle),
|
|
m_nMaxLevelAngle(maxLevelAngle),
|
|
m_nCurrentRate(servoRate),
|
|
m_bMoveActive(false),
|
|
m_bDetachTmrActive(false),
|
|
m_nMinPulseWidth(minPulseWidth),
|
|
m_nMaxPulseWidth(maxPulseWidth)
|
|
{
|
|
setPWMPin(pinPWM);
|
|
m_nOldAngle = (minLevelAngle + maxLevelAngle) / 2;
|
|
}
|
|
|
|
//destructor
|
|
EX_Servo::~EX_Servo()
|
|
{
|
|
|
|
}
|
|
|
|
void EX_Servo::init()
|
|
{
|
|
writeAngleToPin();
|
|
refresh();
|
|
}
|
|
|
|
void EX_Servo::update()
|
|
{
|
|
if (m_bMoveActive) {
|
|
if ((millis() - m_nPrevMillis) > m_nTimeStep) {
|
|
m_nPrevMillis = millis();
|
|
if (m_nCurrentAngle != m_nTargetAngle) {
|
|
if (m_nTargetAngle >= m_nOldAngle) {
|
|
m_nCurrentAngle = m_nCurrentAngle + 1;
|
|
}
|
|
else {
|
|
m_nCurrentAngle = m_nCurrentAngle - 1;
|
|
}
|
|
m_Servo.write(m_nCurrentAngle);
|
|
}
|
|
else {
|
|
m_bMoveActive = false;
|
|
if (st::Executor::debug) {
|
|
Serial.println(F("EX_Servo::update() move complete"));
|
|
}
|
|
if (m_bDetachAfterMove) {
|
|
m_bDetachTmrActive = true;
|
|
}
|
|
refresh();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_bDetachTmrActive) {
|
|
if ((millis() - m_nPrevMillis) > m_nDetachTime) {
|
|
m_bDetachTmrActive = false;
|
|
m_Servo.detach();
|
|
if (st::Executor::debug) {
|
|
Serial.println(F("EX_Servo::update() detach complete"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void EX_Servo::beSmart(const String &str)
|
|
{
|
|
String level = str.substring(str.indexOf(' ') + 1, str.indexOf(':'));
|
|
String rate = str.substring(str.indexOf(':') + 1);
|
|
|
|
level.trim();
|
|
rate.trim();
|
|
|
|
if (st::Executor::debug) {
|
|
Serial.print(F("EX_Servo::beSmart level = "));
|
|
Serial.println(level);
|
|
Serial.print(F("EX_Servo::beSmart rate = "));
|
|
Serial.println(rate);
|
|
}
|
|
|
|
m_nCurrentLevel = int(level.toInt());
|
|
m_nCurrentRate = long(rate.toInt());
|
|
m_nOldAngle = m_nCurrentAngle;
|
|
m_nTargetAngle = map(m_nCurrentLevel, 0, 100, m_nMinLevelAngle, m_nMaxLevelAngle);
|
|
|
|
if (st::Executor::debug) {
|
|
Serial.print(F("EX_Servo::beSmart OldAngle = "));
|
|
Serial.println(m_nOldAngle);
|
|
Serial.print(F("EX_Servo::beSmart TargetAngle = "));
|
|
Serial.println(m_nTargetAngle);
|
|
Serial.print(F("EX_Servo::beSmart CurrentRate = "));
|
|
Serial.println(m_nCurrentRate);
|
|
}
|
|
writeAngleToPin();
|
|
|
|
|
|
}
|
|
|
|
void EX_Servo::refresh()
|
|
{
|
|
Everything::sendSmartString(getName() + " " + String(m_nCurrentLevel) + ":" + String(m_nTargetAngle) + ":" + String(m_nCurrentRate));
|
|
}
|
|
|
|
void EX_Servo::setPWMPin(byte pin)
|
|
{
|
|
m_nPinPWM = pin;
|
|
}
|
|
}
|
|
|
|
#endif
|