From d333a265f488c6fd3c6761cc598482c50d79a222 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Sun, 14 Aug 2022 06:48:03 +1000 Subject: [PATCH 1/3] Add EX-Turntable to PORTX_HAL ready to test --- EXRAIL2MacroReset.h | 2 + EXRAILMacros.h | 1 + IODevice.h | 33 ++++++++++++ IO_EXTurntable.h | 121 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 157 insertions(+) create mode 100644 IO_EXTurntable.h 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 From 0a6d023373226ba813597d710e1e98473536a6e9 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Sun, 14 Aug 2022 06:58:20 +1000 Subject: [PATCH 2/3] Add ready to test --- DCCEXParser.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index e9047a6..e986406 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -67,6 +67,7 @@ const int16_t HASH_KEYWORD_RETRY = 25704; const int16_t HASH_KEYWORD_SPEED28 = -17064; const int16_t HASH_KEYWORD_SPEED128 = 25816; const int16_t HASH_KEYWORD_SERVO=27709; +const int16_t HASH_KEYWORD_TT=2688; const int16_t HASH_KEYWORD_VPIN=-415; const int16_t HASH_KEYWORD_A='A'; const int16_t HASH_KEYWORD_C='C'; @@ -938,6 +939,10 @@ bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[]) break; #endif + case HASH_KEYWORD_TT: // + IODevice::writeAnalogue(p[1], p[2], params>3 ? p[3] : 0); + break; + default: // invalid/unknown break; } From 9f27759a9cb6e1d3e4ac32d92ceabd1d63420b7d Mon Sep 17 00:00:00 2001 From: peteGSX Date: Sun, 14 Aug 2022 07:11:38 +1000 Subject: [PATCH 3/3] Add myEX-Turntable.example.h --- myEX-Turntable.example.h | 101 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 myEX-Turntable.example.h diff --git a/myEX-Turntable.example.h b/myEX-Turntable.example.h new file mode 100644 index 0000000..cbc1e54 --- /dev/null +++ b/myEX-Turntable.example.h @@ -0,0 +1,101 @@ +/************************************************************************************************** + * This is an example automation file to control EX-Turntable using recommended techniques. + ************************************************************************************************** + * INSTRUCTIONS + ************************************************************************************************** + * To use this example file as the starting point for your layout, there are two options: + * + * 1. If you don't have an existing "myAutomation.h" file, simply rename "myEX-Turntable.example.h" to + * "myAutomation.h". + * 2. If you have an existing "myAutomation.h" file, rename "myEX-Turntable.example.h" to "myEX-Turntable.h", + * and then include it by adding the line below at the end of your existing "myAutomation.h", on a + * line of its own: + * + * #include "myEX-Turntable.h" + * + * Note that there are further instructions in the documentation at https://dcc-ex.com/. + *************************************************************************************************/ + +/************************************************************************************************** + * The MOVETT() command below will automatically move your turntable to the defined step position on + * start up. + * + * If you do not wish this to occur, simply comment the line out. + * + * NOTE: If you are including this file at the end of an existing "myAutomation.h" file, you will likely + * need to move this line to the beginning of your existing "myAutomation.h" file in order for it to + * be effective. + *************************************************************************************************/ +MOVETT(600, 114, Turn) +DONE + +// For Conductor level users who wish to just use EX-Turntable, you don't need to understand this +// and can move to defining the turntable positions below. You must, however, ensure this remains +// before any position definitions or you will get compile errors when uploading. +// +// Definition of the EX_TURNTABLE macro to correctly create the ROUTEs required for each position. +// This includes RESERVE()/FREE() to protect any automation activities. +// +#define EX_TURNTABLE(route_id, reserve_id, vpin, steps, activity, desc) \ + ROUTE(route_id, desc) \ + RESERVE(reserve_id) \ + MOVETT(vpin, steps, activity) \ + WAITFOR(vpin) \ + FREE(reserve_id) \ + DONE + +/************************************************************************************************** + * TURNTABLE POSITION DEFINITIONS + *************************************************************************************************/ +// EX_TURNTABLE(route_id, reserve_id, vpin, steps, activity, desc) +// +// route_id = A unique number for each defined route, the route is what appears in throttles +// reserve_id = A unique reservation number (0 - 255) to ensure nothing interferes with automation +// vpin = The Vpin defined for the Turntable-EX device driver, default is 600 +// steps = The target step position +// activity = The activity performed for this ROUTE (Note do not enclose in quotes "") +// desc = Description that will appear in throttles (Must use quotes "") +// +EX_TURNTABLE(TTRoute1, Turntable, 600, 114, Turn, "Position 1") +EX_TURNTABLE(TTRoute2, Turntable, 600, 227, Turn, "Position 2") +EX_TURNTABLE(TTRoute3, Turntable, 600, 341, Turn, "Position 3") +EX_TURNTABLE(TTRoute4, Turntable, 600, 2159, Turn, "Position 4") +EX_TURNTABLE(TTRoute5, Turntable, 600, 2273, Turn, "Position 5") +EX_TURNTABLE(TTRoute6, Turntable, 600, 2386, Turn, "Position 6") +EX_TURNTABLE(TTRoute7, Turntable, 600, 0, Home, "Home turntable") + +// Pre-defined aliases to ensure unique IDs are used. +// Turntable reserve ID, valid is 0 - 255 +ALIAS(Turntable, 255) + +// Turntable ROUTE ID reservations, using for uniqueness: +ALIAS(TTRoute1) +ALIAS(TTRoute2) +ALIAS(TTRoute3) +ALIAS(TTRoute4) +ALIAS(TTRoute5) +ALIAS(TTRoute6) +ALIAS(TTRoute7) +ALIAS(TTRoute8) +ALIAS(TTRoute9) +ALIAS(TTRoute10) +ALIAS(TTRoute11) +ALIAS(TTRoute12) +ALIAS(TTRoute13) +ALIAS(TTRoute14) +ALIAS(TTRoute15) +ALIAS(TTRoute16) +ALIAS(TTRoute17) +ALIAS(TTRoute18) +ALIAS(TTRoute19) +ALIAS(TTRoute20) +ALIAS(TTRoute21) +ALIAS(TTRoute22) +ALIAS(TTRoute23) +ALIAS(TTRoute24) +ALIAS(TTRoute25) +ALIAS(TTRoute26) +ALIAS(TTRoute27) +ALIAS(TTRoute28) +ALIAS(TTRoute29) +ALIAS(TTRoute30)