From 0cf81d589ec2d1ac5aafb6eb1272c6e2447e5dfb Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Mon, 19 Jun 2023 08:25:20 +1000 Subject: [PATCH 1/3] Add _writeAnalogue() --- IO_RotaryEncoder.h | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/IO_RotaryEncoder.h b/IO_RotaryEncoder.h index 00a8249..2c5a5ec 100644 --- a/IO_RotaryEncoder.h +++ b/IO_RotaryEncoder.h @@ -1,4 +1,5 @@ /* + * © 2023, Peter Cole. All rights reserved. * © 2022, Peter Cole. All rights reserved. * * This file is part of EX-CommandStation @@ -28,9 +29,23 @@ * ONCHANGE(vpin) - flag when the rotary encoder position has changed from the previous position * IFRE(vpin, position) - test to see if specified rotary encoder position has been received * -* Further to this, feedback can be sent to the rotary encoder by using 2 Vpins, and sending a SET()/RESET() to the second Vpin. +* Feedback can also be sent to the rotary encoder by using 2 Vpins, and sending a SET()/RESET() to the second Vpin. * A SET(vpin) will flag that a turntable (or anything else) is in motion, and a RESET(vpin) that the motion has finished. * +* In addition, defining a third Vpin will allow a position number to be sent so that when an EXRAIL automation or some other +* activity has moved a turntable, the position can be reflected in the rotary encoder software. This can be accomplished +* using the EXRAIL SERVO(vpin, position, profile) command, where: +* - vpin = the third defined Vpin (any other is ignored) +* - position = the defined position in the DCC-EX Rotary Encoder software, 0 (Home) to 255 +* - profile = Must be defined as per the SERVO() command, but is ignored as it has no relevance +* +* Defining in myAutomation.h requires the device driver to be included in addition to the HAL() statement. Examples: +* +* #include "IO_RotaryEncoder.h" +* HAL(RotaryEncoder, 700, 1, 0x70) // Define single Vpin, no feedback or position sent to rotary encoder software +* HAL(RotaryEncoder, 700, 2, 0x70) // Define two Vpins, feedback only sent to rotary encoder software +* HAL(RotaryEncoder, 700, 3, 0x70) // Define three Vpins, can send feedback and position update to rotary encoder software +* * Refer to the documentation for further information including the valid activities and examples. */ @@ -103,6 +118,15 @@ private: I2CManager.write(_I2CAddress, _feedbackBuffer, 2); } } + + void _writeAnalogue(VPIN vpin, int position, uint8_t profile, uint16_t duration) override { + if (vpin == _firstVpin + 2) { + if (position >= 0 && position <= 255) { + byte _positionBuffer[2] = {RE_MOVE, position}; + I2CManager.write(_I2CAddress, _positionBuffer, 2); + } + } + } void _display() override { DIAG(F("Rotary Encoder I2C:%s v%d.%d.%d Configured on VPIN:%u-%d %S"), _I2CAddress.toString(), _majorVer, _minorVer, _patchVer, @@ -120,6 +144,7 @@ private: enum { RE_VER = 0xA0, // Flag to retrieve rotary encoder version from the device RE_OP = 0xA1, // Flag for normal operation + RE_MOVE = 0xA2, // Flag for sending a position update }; }; From 955ff4f96d591084c086ec48e5f3c5a185122abf Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Mon, 19 Jun 2023 08:25:20 +1000 Subject: [PATCH 2/3] Add _writeAnalogue() --- IO_RotaryEncoder.h | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/IO_RotaryEncoder.h b/IO_RotaryEncoder.h index 00a8249..2c5a5ec 100644 --- a/IO_RotaryEncoder.h +++ b/IO_RotaryEncoder.h @@ -1,4 +1,5 @@ /* + * © 2023, Peter Cole. All rights reserved. * © 2022, Peter Cole. All rights reserved. * * This file is part of EX-CommandStation @@ -28,9 +29,23 @@ * ONCHANGE(vpin) - flag when the rotary encoder position has changed from the previous position * IFRE(vpin, position) - test to see if specified rotary encoder position has been received * -* Further to this, feedback can be sent to the rotary encoder by using 2 Vpins, and sending a SET()/RESET() to the second Vpin. +* Feedback can also be sent to the rotary encoder by using 2 Vpins, and sending a SET()/RESET() to the second Vpin. * A SET(vpin) will flag that a turntable (or anything else) is in motion, and a RESET(vpin) that the motion has finished. * +* In addition, defining a third Vpin will allow a position number to be sent so that when an EXRAIL automation or some other +* activity has moved a turntable, the position can be reflected in the rotary encoder software. This can be accomplished +* using the EXRAIL SERVO(vpin, position, profile) command, where: +* - vpin = the third defined Vpin (any other is ignored) +* - position = the defined position in the DCC-EX Rotary Encoder software, 0 (Home) to 255 +* - profile = Must be defined as per the SERVO() command, but is ignored as it has no relevance +* +* Defining in myAutomation.h requires the device driver to be included in addition to the HAL() statement. Examples: +* +* #include "IO_RotaryEncoder.h" +* HAL(RotaryEncoder, 700, 1, 0x70) // Define single Vpin, no feedback or position sent to rotary encoder software +* HAL(RotaryEncoder, 700, 2, 0x70) // Define two Vpins, feedback only sent to rotary encoder software +* HAL(RotaryEncoder, 700, 3, 0x70) // Define three Vpins, can send feedback and position update to rotary encoder software +* * Refer to the documentation for further information including the valid activities and examples. */ @@ -103,6 +118,15 @@ private: I2CManager.write(_I2CAddress, _feedbackBuffer, 2); } } + + void _writeAnalogue(VPIN vpin, int position, uint8_t profile, uint16_t duration) override { + if (vpin == _firstVpin + 2) { + if (position >= 0 && position <= 255) { + byte _positionBuffer[2] = {RE_MOVE, position}; + I2CManager.write(_I2CAddress, _positionBuffer, 2); + } + } + } void _display() override { DIAG(F("Rotary Encoder I2C:%s v%d.%d.%d Configured on VPIN:%u-%d %S"), _I2CAddress.toString(), _majorVer, _minorVer, _patchVer, @@ -120,6 +144,7 @@ private: enum { RE_VER = 0xA0, // Flag to retrieve rotary encoder version from the device RE_OP = 0xA1, // Flag for normal operation + RE_MOVE = 0xA2, // Flag for sending a position update }; }; From 0be9af22707ba84a770d5a57ed865956a1d1bdec Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Tue, 20 Jun 2023 09:37:19 +1000 Subject: [PATCH 3/3] Update some logic --- IO_RotaryEncoder.h | 106 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 80 insertions(+), 26 deletions(-) diff --git a/IO_RotaryEncoder.h b/IO_RotaryEncoder.h index 2c5a5ec..3391f6d 100644 --- a/IO_RotaryEncoder.h +++ b/IO_RotaryEncoder.h @@ -59,50 +59,98 @@ class RotaryEncoder : public IODevice { public: - // Constructor - RotaryEncoder(VPIN firstVpin, int nPins, I2CAddress i2cAddress){ - _firstVpin = firstVpin; - _nPins = nPins; - _I2CAddress = i2cAddress; - addDevice(this); - } + static void create(VPIN firstVpin, int nPins, I2CAddress i2cAddress) { if (checkNoOverlap(firstVpin, nPins, i2cAddress)) new RotaryEncoder(firstVpin, nPins, i2cAddress); } private: + // Constructor + RotaryEncoder(VPIN firstVpin, int nPins, I2CAddress i2cAddress){ + _firstVpin = firstVpin; + _nPins = nPins; + if (_nPins > 3) { + _nPins = 3; + DIAG(F("RotaryEncoder WARNING:%d vpins defined, only 3 supported"), _nPins); + } + _I2CAddress = i2cAddress; + addDevice(this); + } + // Initiate the device void _begin() { + uint8_t _status; + // Attempt to initilalise device I2CManager.begin(); if (I2CManager.exists(_I2CAddress)) { - byte _getVersion[1] = {RE_VER}; - I2CManager.read(_I2CAddress, _versionBuffer, 3, _getVersion, 1); - _majorVer = _versionBuffer[0]; - _minorVer = _versionBuffer[1]; - _patchVer = _versionBuffer[2]; - _buffer[0] = RE_OP; - I2CManager.write(_I2CAddress, _buffer, 1); + // Send RE_OP, must receive RE_OP to be online + _sendBuffer[0] = RE_OP; + _status = I2CManager.read(_I2CAddress, _rcvBuffer, 1, _sendBuffer, 1); + if (_status == I2C_STATUS_OK) { + if (_rcvBuffer[0] == RE_OP) { + _sendBuffer[0] = RE_VER; + if (I2CManager.read(_I2CAddress, _versionBuffer, 3, _sendBuffer, 1) == I2C_STATUS_OK) { + _majorVer = _versionBuffer[0]; + _minorVer = _versionBuffer[1]; + _patchVer = _versionBuffer[2]; + } + } else { + DIAG(F("RotaryEncoder I2C:%s garbage received: %d"), _I2CAddress.toString(), _rcvBuffer[0]); + _deviceState = DEVSTATE_FAILED; + return; + } + } else { + DIAG(F("RotaryEncoder I2C:%s ERROR connecting"), _I2CAddress.toString()); + _deviceState = DEVSTATE_FAILED; + return; + } + // byte _getVersion[1] = {RE_VER}; + // I2CManager.read(_I2CAddress, _versionBuffer, 3, _getVersion, 1); + // _majorVer = _versionBuffer[0]; + // _minorVer = _versionBuffer[1]; + // _patchVer = _versionBuffer[2]; + // _buffer[0] = RE_OP; + // I2CManager.write(_I2CAddress, _buffer, 1); #ifdef DIAG_IO _display(); #endif } else { - _deviceState = DEVSTATE_FAILED; + DIAG(F("RotaryEncoder I2C:%s device not found"), _I2CAddress.toString()); + _deviceState = DEVSTATE_FAILED; } } void _loop(unsigned long currentMicros) override { - I2CManager.read(_I2CAddress, _buffer, 1); - _position = _buffer[0]; - // This here needs to have a change check, ie. position is a different value. - #if defined(EXRAIL_ACTIVE) + if (_deviceState == DEVSTATE_FAILED) return; // Return if device has failed + if (_i2crb.isBusy()) return; // Return if I2C operation still in progress + + if (currentMicros - _lastPositionRead > _positionRefresh) { + _lastPositionRead = currentMicros; + _sendBuffer[0] = RE_READ; + I2CManager.read(_I2CAddress, _rcvBuffer, 1, _sendBuffer, 1, &_i2crb); // Read position from encoder + _position = _rcvBuffer[0]; + // If EXRAIL is active, we need to trigger the ONCHANGE() event handler if it's in use +#if defined(EXRAIL_ACTIVE) if (_position != _previousPosition) { _previousPosition = _position; - RMFT2::changeEvent(_firstVpin,1); + RMFT2::changeEvent(_firstVpin, 1); } else { - RMFT2::changeEvent(_firstVpin,0); + RMFT2::changeEvent(_firstVpin, 0); } - #endif - delayUntil(currentMicros + 100000); +#endif + } + // I2CManager.read(_I2CAddress, _buffer, 1); + // _position = _buffer[0]; + // // This here needs to have a change check, ie. position is a different value. + // #if defined(EXRAIL_ACTIVE) + // if (_position != _previousPosition) { + // _previousPosition = _position; + // RMFT2::changeEvent(_firstVpin,1); + // } else { + // RMFT2::changeEvent(_firstVpin,0); + // } + // #endif + // delayUntil(currentMicros + 100000); } // Device specific read function @@ -122,7 +170,8 @@ private: void _writeAnalogue(VPIN vpin, int position, uint8_t profile, uint16_t duration) override { if (vpin == _firstVpin + 2) { if (position >= 0 && position <= 255) { - byte _positionBuffer[2] = {RE_MOVE, position}; + byte newPosition = position & 0xFF; + byte _positionBuffer[2] = {RE_MOVE, newPosition}; I2CManager.write(_I2CAddress, _positionBuffer, 2); } } @@ -136,15 +185,20 @@ private: int8_t _position; int8_t _previousPosition = 0; uint8_t _versionBuffer[3]; - uint8_t _buffer[1]; + uint8_t _sendBuffer[1]; + uint8_t _rcvBuffer[1]; uint8_t _majorVer = 0; uint8_t _minorVer = 0; uint8_t _patchVer = 0; + I2CRB _i2crb; + unsigned long _lastPositionRead = 0; + const unsigned long _positionRefresh = 100000UL; // Delay refreshing position for 100ms enum { RE_VER = 0xA0, // Flag to retrieve rotary encoder version from the device RE_OP = 0xA1, // Flag for normal operation - RE_MOVE = 0xA2, // Flag for sending a position update + RE_MOVE = 0xA2, // Flag for sending a position update from the device driver to the encoder + RE_READ = 0xA3, // Flag to read the current position of the encoder }; };