st-anything/lib/ST_Anything/EX_Servo.cpp

199 lines
6.8 KiB
C++
Raw Normal View History

2023-03-11 14:11:03 +00:00
//******************************************************************************************
// 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