From a30311caedde2599b3da9da0a4268c40f4fcdec5 Mon Sep 17 00:00:00 2001 From: Sergei Kotlyachkov Date: Thu, 9 Nov 2023 21:16:17 -0500 Subject: [PATCH] Refactor usage of delay() into an Helper IO class that schedules pin bouncing back --- IO_ScheduledPin.h | 115 ++++++++++++++++++++++++++++++++++++++++++++++ Turnouts.cpp | 19 ++++++-- 2 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 IO_ScheduledPin.h diff --git a/IO_ScheduledPin.h b/IO_ScheduledPin.h new file mode 100644 index 0000000..e9040e8 --- /dev/null +++ b/IO_ScheduledPin.h @@ -0,0 +1,115 @@ +/* + * © 2023, Sergei Kotlyachkov. All rights reserved. + * + * This file is part of DCC++EX API + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * It is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CommandStation. If not, see . + */ + + +#ifndef IO_SCHEDULED_PIN_H +#define IO_SCHEDULED_PIN_H + +#include "IODevice.h" +#include +#include "defines.h" + +/** + * Bounces back single Arduino Pin to specified state after set period of time. + * + * It will establish itself as owner of the pin over ArduinoPins class that typically responds to it and + * activates itself during loop() phase. It restores scheduled state and does not try again until + * another write() + * + * Example usage: + * Create: ScheduledPin::create(5, LOW, 20000); + * + * Then, when neeeded, just call: + * IODevice::write(5, HIGH); // this will call fastWriteDigital(5, HIGH) + * + * In 20 milliseconds, it will also call fastWriteDigital(5, LOW) + * + * In edge case where write() is called twice before responding in the loop, + * the schedule will restart and double the bounce back time. + */ +class ScheduledPin : public IODevice { +private: + int _scheduledValue; + uint32_t _durationMicros; + +public: + // Static function to handle create calls. + static void create(VPIN pin, int scheduledValue, uint32_t durationMicros) { + new ScheduledPin(pin, scheduledValue, durationMicros); + } + +protected: + // Constructor. + ScheduledPin(VPIN pin, int scheduledValue, uint32_t durationMicros) : IODevice(pin, 1) { + _scheduledValue = scheduledValue; + _durationMicros = durationMicros; + // Typically returned device will be ArduinoPins + IODevice* controlledDevice = IODevice::findDevice(pin); + if (controlledDevice != NULL) { + addDevice(this, controlledDevice); + } + else { + DIAG(F("ScheduledPin Controlled device not found for pin:%d"), pin); + _deviceState = DEVSTATE_FAILED; + } + } + + // Device-specific initialisation + void _begin() override { + #ifdef DIAG_IO + _display(); + #endif + pinMode(_firstVpin, OUTPUT); + ArduinoPins::fastWriteDigital(_firstVpin, _scheduledValue); + } + + void _write(VPIN vpin, int value) override { + if (_deviceState == DEVSTATE_FAILED) return; + if (vpin != _firstVpin) { + #ifdef DIAG_IO + DIAG(F("ScheduledPin Error VPIN:%u not equal to %u"), vpin, _firstVpin); + #endif + return; + } + #ifdef DIAG_IO + DIAG(F("ScheduledPin Write VPIN:%u Value:%d"), vpin, value); + #endif + unsigned long currentMicros = micros(); + delayUntil(currentMicros + _durationMicros); + ArduinoPins::fastWriteDigital(_firstVpin, value); + } + + + void _loop(unsigned long currentMicros) { + if (_deviceState == DEVSTATE_FAILED) return; + #ifdef DIAG_IO + DIAG(F("ScheduledPin Write VPIN:%u Value:%d"), _firstVpin, _scheduledValue); + #endif + ArduinoPins::fastWriteDigital(_firstVpin, _scheduledValue); + delayUntil(currentMicros + 0x7fffffff); // Largest time in the future! Effectively disable _loop calls. + } + + // Display information about the device, and perhaps its current condition (e.g. active, disabled etc). + void _display() { + DIAG(F("ScheduledPin Configured:%u value=%d duration=%ld"), (int)_firstVpin, + (int)_firstVpin, _scheduledValue, _durationMicros); + } +}; + +#endif // IO_SCHEDULED_PIN_H diff --git a/Turnouts.cpp b/Turnouts.cpp index 4b44ad4..5957d50 100644 --- a/Turnouts.cpp +++ b/Turnouts.cpp @@ -36,6 +36,10 @@ #include "LCN.h" #ifdef EESTOREDEBUG #include "DIAG.h" +#endif + +#ifndef IO_NO_HAL +#include "IO_ScheduledPin.h" #endif /* @@ -493,6 +497,14 @@ _hbridgeTurnoutData.pin1 = pin1; _hbridgeTurnoutData.pin2 = pin2; _hbridgeTurnoutData.millisDelay = millisDelay; +#ifndef IO_NO_HAL + // HARD LIMIT to maximum 0.5 second to avoid burning the coil + // Also note 1000x multiplier because ScheduledPin works with microSeconds. + ScheduledPin::create(pin1, LOW, 1000*min(millisDelay, 500)); + ScheduledPin::create(pin2, LOW, 1000*min(millisDelay, 500)); +#else + DIAG(F("H-Brdige Turnout %d will be disabled because HAL is off"), id); +#endif } // Create function @@ -545,10 +557,11 @@ void HBridgeTurnout::turnUpDown(VPIN pin) { // HBridge turnouts require very small, prescribed time to keep pin1 or pin2 in HIGH state. // Otherwise internal coil of the turnout will burn. + // If HAL is disabled (and therefore SchedulePin class), we can not turn this on, + // otherwise coil will burn and device will be lost. +#ifndef IO_NO_HAL IODevice::write(pin, HIGH); - // HARD LIMIT to maximum 0.5 second to avoid burning the coil - delay(min(_hbridgeTurnoutData.millisDelay, 500)); - IODevice::write(pin, LOW); +#endif } bool HBridgeTurnout::setClosedInternal(bool close) {