diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index fb0ea23..0211e22 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -78,6 +78,7 @@ #undef LATCH #undef LCD #undef LCN +#undef MOVETT #undef ONACTIVATE #undef ONACTIVATEL #undef ONAMBER @@ -187,6 +188,7 @@ #define LATCH(sensor_id) #define LCD(row,msg) #define LCN(msg) +#define MOVETT(id,steps,activity) #define ONACTIVATE(addr,subaddr) #define ONACTIVATEL(linear) #define ONAMBER(signal_id) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index f2b5bca..d5424c6 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -267,6 +267,7 @@ const FLASH int16_t RMFT2::SignalDefinitions[] = { #define LATCH(sensor_id) OPCODE_LATCH,V(sensor_id), #define LCD(id,msg) PRINT(msg) #define LCN(msg) PRINT(msg) +#define MOVETT(id,steps,activity) OPCODE_SERVO,V(id),OPCODE_PAD,V(steps),OPCODE_PAD,V(EXTurntable::activity),OPCODE_PAD,V(0), #define ONACTIVATE(addr,subaddr) OPCODE_ONACTIVATE,V(addr<<2|subaddr), #define ONACTIVATEL(linear) OPCODE_ONACTIVATE,V(linear+3), #define ONAMBER(signal_id) OPCODE_ONAMBER,V(signal_id), diff --git a/IODevice.h b/IODevice.h index 8b786c4..2a3437c 100644 --- a/IODevice.h +++ b/IODevice.h @@ -364,6 +364,39 @@ private: uint8_t *_pinInUse; }; +///////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * IODevice subclass for EX-Turntable. + */ + +class EXTurntable : public IODevice { +public: + static void create(VPIN firstVpin, int nPins, uint8_t I2CAddress); + // Constructor + EXTurntable(VPIN firstVpin, int nPins, uint8_t I2CAddress); + enum ActivityNumber : uint8_t { + Turn = 0, // Rotate turntable, maintain phase + Turn_PInvert = 1, // Rotate turntable, invert phase + Home = 2, // Initiate homing + Calibrate = 3, // Initiate calibration sequence + LED_On = 4, // Turn LED on + LED_Slow = 5, // Set LED to a slow blink + LED_Fast = 6, // Set LED to a fast blink + LED_Off = 7, // Turn LED off + Acc_On = 8, // Turn accessory pin on + Acc_Off = 9, // Turn accessory pin off + }; + +private: + // Device-specific write function. + void _begin() override; + void _loop(unsigned long currentMicros) override; + int _read(VPIN vpin) override; + void _writeAnalogue(VPIN vpin, int value, uint8_t activity, uint16_t duration) override; + void _display() override; + uint8_t _stepperStatus; +}; + ///////////////////////////////////////////////////////////////////////////////////////////////////// #include "IO_MCP23008.h" diff --git a/IO_EXTurntable.h b/IO_EXTurntable.h new file mode 100644 index 0000000..ea3dcb0 --- /dev/null +++ b/IO_EXTurntable.h @@ -0,0 +1,121 @@ +/* + * © 2021, Peter Cole. All rights reserved. + * + * This file is part of CommandStation-EX + * + * 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 . +*/ + +/* +* The IO_EXTurntable device driver is used to control a turntable via an Arduino with a stepper motor over I2C. +* +* The EX-Turntable code lives in a separate repo (https://github.com/DCC-EX/Turntable-EX) and contains the stepper motor logic. +* +* This device driver sends a step position to Turntable-EX to indicate the step position to move to using either of these commands: +* in the serial console +* MOVETT(vpin, steps, activity) in EX-RAIL +* Refer to the documentation for further information including the valid activities. +*/ + +#ifndef IO_EXTurntable_h +#define IO_EXTurntable_h + +#include "IODevice.h" +#include "I2CManager.h" +#include "DIAG.h" + +void EXTurntable::create(VPIN firstVpin, int nPins, uint8_t I2CAddress) { + new EXTurntable(firstVpin, nPins, I2CAddress); +} + +// Constructor +EXTurntable::EXTurntable(VPIN firstVpin, int nPins, uint8_t I2CAddress) { + _firstVpin = firstVpin; + _nPins = nPins; + _I2CAddress = I2CAddress; + addDevice(this); +} + +// Initialisation of TurntableEX +void EXTurntable::_begin() { + I2CManager.begin(); + I2CManager.setClock(1000000); + if (I2CManager.exists(_I2CAddress)) { +#ifdef DIAG_IO + _display(); +#endif + } else { + _deviceState = DEVSTATE_FAILED; + } +} + +// Processing loop to obtain status of stepper +// 0 = finished moving and in correct position +// 1 = still moving +void EXTurntable::_loop(unsigned long currentMicros) { + uint8_t readBuffer[1]; + I2CManager.read(_I2CAddress, readBuffer, 1); + _stepperStatus = readBuffer[0]; + // DIAG(F("Turntable-EX returned status: %d"), _stepperStatus); + delayUntil(currentMicros + 500000); // Wait 500ms before checking again, turntables turn slowly +} + +// Read returns status as obtained in our loop. +// Return false if our status value is invalid. +int EXTurntable::_read(VPIN vpin) { + if (_deviceState == DEVSTATE_FAILED) return 0; + // DIAG(F("_read status: %d"), _stepperStatus); + if (_stepperStatus > 1) { + return false; + } else { + return _stepperStatus; + } +} + +// writeAnalogue to send the steps and activity to Turntable-EX. +// Sends 3 bytes containing the MSB and LSB of the step count, and activity. +// value contains the steps, bit shifted to MSB + LSB. +// activity contains the activity flag as per this list: +// +// Turn = 0, // Rotate turntable, maintain phase +// Turn_PInvert = 1, // Rotate turntable, invert phase +// Home = 2, // Initiate homing +// Calibrate = 3, // Initiate calibration sequence +// LED_On = 4, // Turn LED on +// LED_Slow = 5, // Set LED to a slow blink +// LED_Fast = 6, // Set LED to a fast blink +// LED_Off = 7, // Turn LED off +// Acc_On = 8, // Turn accessory pin on +// Acc_Off = 9 // Turn accessory pin off +void EXTurntable::_writeAnalogue(VPIN vpin, int value, uint8_t activity, uint16_t duration) { + if (_deviceState == DEVSTATE_FAILED) return; + uint8_t stepsMSB = value >> 8; + uint8_t stepsLSB = value & 0xFF; +#ifdef DIAG_IO + DIAG(F("TurntableEX WriteAnalogue Vpin:%d Value:%d Activity:%d Duration:%d"), + vpin, value, activity, duration); + DIAG(F("I2CManager write I2C Address:%d stepsMSB:%d stepsLSB:%d activity:%d"), + _I2CAddress, stepsMSB, stepsLSB, activity); +#endif + _stepperStatus = 1; // Tell the device driver Turntable-EX is busy + I2CManager.write(_I2CAddress, 3, stepsMSB, stepsLSB, activity); +} + +// Display Turnetable-EX device driver info. +void EXTurntable::_display() { + DIAG(F("TurntableEX I2C:x%x Configured on Vpins:%d-%d %S"), _I2CAddress, (int)_firstVpin, + (int)_firstVpin+_nPins-1, (_deviceState==DEVSTATE_FAILED) ? F("OFFLINE") : F("")); +} + +#endif