mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-12-24 13:21:23 +01:00
Merge pull request #285 from DCC-EX:add-rotary-encoder
New working rotary encoder branch
This commit is contained in:
commit
ace9c1642a
12
EXRAIL2.cpp
12
EXRAIL2.cpp
@ -91,6 +91,7 @@ LookList * RMFT2::onDeactivateLookup=NULL;
|
|||||||
LookList * RMFT2::onRedLookup=NULL;
|
LookList * RMFT2::onRedLookup=NULL;
|
||||||
LookList * RMFT2::onAmberLookup=NULL;
|
LookList * RMFT2::onAmberLookup=NULL;
|
||||||
LookList * RMFT2::onGreenLookup=NULL;
|
LookList * RMFT2::onGreenLookup=NULL;
|
||||||
|
LookList * RMFT2::onChangeLookup=NULL;
|
||||||
|
|
||||||
#define GET_OPCODE GETHIGHFLASH(RMFT2::RouteCode,progCounter)
|
#define GET_OPCODE GETHIGHFLASH(RMFT2::RouteCode,progCounter)
|
||||||
#define SKIPOP progCounter+=3
|
#define SKIPOP progCounter+=3
|
||||||
@ -173,6 +174,7 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) {
|
|||||||
onRedLookup=LookListLoader(OPCODE_ONRED);
|
onRedLookup=LookListLoader(OPCODE_ONRED);
|
||||||
onAmberLookup=LookListLoader(OPCODE_ONAMBER);
|
onAmberLookup=LookListLoader(OPCODE_ONAMBER);
|
||||||
onGreenLookup=LookListLoader(OPCODE_ONGREEN);
|
onGreenLookup=LookListLoader(OPCODE_ONGREEN);
|
||||||
|
onChangeLookup=LookListLoader(OPCODE_ONCHANGE);
|
||||||
|
|
||||||
// Second pass startup, define any turnouts or servos, set signals red
|
// Second pass startup, define any turnouts or servos, set signals red
|
||||||
// add sequences onRoutines to the lookups
|
// add sequences onRoutines to the lookups
|
||||||
@ -746,6 +748,10 @@ void RMFT2::loop2() {
|
|||||||
skipIf=readSensor(operand);
|
skipIf=readSensor(operand);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case OPCODE_IFRE: // do next operand if rotary encoder != position
|
||||||
|
skipIf=IODevice::readAnalogue(operand)!=(int)(getOperand(1));
|
||||||
|
break;
|
||||||
|
|
||||||
case OPCODE_IFRANDOM: // do block on random percentage
|
case OPCODE_IFRANDOM: // do block on random percentage
|
||||||
skipIf=(uint8_t)micros() >= operand * 255/100;
|
skipIf=(uint8_t)micros() >= operand * 255/100;
|
||||||
break;
|
break;
|
||||||
@ -968,6 +974,7 @@ void RMFT2::loop2() {
|
|||||||
case OPCODE_ONRED:
|
case OPCODE_ONRED:
|
||||||
case OPCODE_ONAMBER:
|
case OPCODE_ONAMBER:
|
||||||
case OPCODE_ONGREEN:
|
case OPCODE_ONGREEN:
|
||||||
|
case OPCODE_ONCHANGE:
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1095,6 +1102,11 @@ void RMFT2::activateEvent(int16_t addr, bool activate) {
|
|||||||
else handleEvent(F("DEACTIVATE"),onDeactivateLookup,addr);
|
else handleEvent(F("DEACTIVATE"),onDeactivateLookup,addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RMFT2::changeEvent(int16_t vpin, bool change) {
|
||||||
|
// Hunt for an ONCHANGE for this sensor
|
||||||
|
if (change) handleEvent(F("CHANGE"),onChangeLookup,vpin);
|
||||||
|
}
|
||||||
|
|
||||||
void RMFT2::handleEvent(const FSH* reason,LookList* handlers, int16_t id) {
|
void RMFT2::handleEvent(const FSH* reason,LookList* handlers, int16_t id) {
|
||||||
int pc= handlers->find(id);
|
int pc= handlers->find(id);
|
||||||
if (pc<0) return;
|
if (pc<0) return;
|
||||||
|
@ -54,6 +54,7 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,
|
|||||||
OPCODE_ENDTASK,OPCODE_ENDEXRAIL,
|
OPCODE_ENDTASK,OPCODE_ENDEXRAIL,
|
||||||
OPCODE_SET_TRACK,
|
OPCODE_SET_TRACK,
|
||||||
OPCODE_ONRED,OPCODE_ONAMBER,OPCODE_ONGREEN,
|
OPCODE_ONRED,OPCODE_ONAMBER,OPCODE_ONGREEN,
|
||||||
|
OPCODE_ONCHANGE,
|
||||||
|
|
||||||
// OPcodes below this point are skip-nesting IF operations
|
// OPcodes below this point are skip-nesting IF operations
|
||||||
// placed here so that they may be skipped as a group
|
// placed here so that they may be skipped as a group
|
||||||
@ -64,7 +65,8 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,
|
|||||||
OPCODE_IFTIMEOUT,
|
OPCODE_IFTIMEOUT,
|
||||||
OPCODE_IF,OPCODE_IFNOT,
|
OPCODE_IF,OPCODE_IFNOT,
|
||||||
OPCODE_IFRANDOM,OPCODE_IFRESERVE,
|
OPCODE_IFRANDOM,OPCODE_IFRESERVE,
|
||||||
OPCODE_IFCLOSED,OPCODE_IFTHROWN
|
OPCODE_IFCLOSED,OPCODE_IFTHROWN,
|
||||||
|
OPCODE_IFRE,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum thrunger: byte {
|
enum thrunger: byte {
|
||||||
@ -113,6 +115,7 @@ class LookList {
|
|||||||
static void createNewTask(int route, uint16_t cab);
|
static void createNewTask(int route, uint16_t cab);
|
||||||
static void turnoutEvent(int16_t id, bool closed);
|
static void turnoutEvent(int16_t id, bool closed);
|
||||||
static void activateEvent(int16_t addr, bool active);
|
static void activateEvent(int16_t addr, bool active);
|
||||||
|
static void changeEvent(int16_t id, bool change);
|
||||||
static const int16_t SERVO_SIGNAL_FLAG=0x4000;
|
static const int16_t SERVO_SIGNAL_FLAG=0x4000;
|
||||||
static const int16_t ACTIVE_HIGH_SIGNAL_FLAG=0x2000;
|
static const int16_t ACTIVE_HIGH_SIGNAL_FLAG=0x2000;
|
||||||
static const int16_t DCC_SIGNAL_FLAG=0x1000;
|
static const int16_t DCC_SIGNAL_FLAG=0x1000;
|
||||||
@ -169,6 +172,7 @@ private:
|
|||||||
static LookList * onRedLookup;
|
static LookList * onRedLookup;
|
||||||
static LookList * onAmberLookup;
|
static LookList * onAmberLookup;
|
||||||
static LookList * onGreenLookup;
|
static LookList * onGreenLookup;
|
||||||
|
static LookList * onChangeLookup;
|
||||||
|
|
||||||
// Local variables - exist for each instance/task
|
// Local variables - exist for each instance/task
|
||||||
RMFT2 *next; // loop chain
|
RMFT2 *next; // loop chain
|
||||||
|
@ -72,6 +72,7 @@
|
|||||||
#undef IFRESERVE
|
#undef IFRESERVE
|
||||||
#undef IFTHROWN
|
#undef IFTHROWN
|
||||||
#undef IFTIMEOUT
|
#undef IFTIMEOUT
|
||||||
|
#undef IFRE
|
||||||
#undef INVERT_DIRECTION
|
#undef INVERT_DIRECTION
|
||||||
#undef JOIN
|
#undef JOIN
|
||||||
#undef KILLALL
|
#undef KILLALL
|
||||||
@ -88,6 +89,7 @@
|
|||||||
#undef ONGREEN
|
#undef ONGREEN
|
||||||
#undef ONRED
|
#undef ONRED
|
||||||
#undef ONTHROW
|
#undef ONTHROW
|
||||||
|
#undef ONCHANGE
|
||||||
#undef PARSE
|
#undef PARSE
|
||||||
#undef PAUSE
|
#undef PAUSE
|
||||||
#undef PIN_TURNOUT
|
#undef PIN_TURNOUT
|
||||||
@ -185,6 +187,7 @@
|
|||||||
#define IFTHROWN(turnout_id)
|
#define IFTHROWN(turnout_id)
|
||||||
#define IFRESERVE(block)
|
#define IFRESERVE(block)
|
||||||
#define IFTIMEOUT
|
#define IFTIMEOUT
|
||||||
|
#define IFRE(sensor_id,value)
|
||||||
#define INVERT_DIRECTION
|
#define INVERT_DIRECTION
|
||||||
#define JOIN
|
#define JOIN
|
||||||
#define KILLALL
|
#define KILLALL
|
||||||
@ -201,6 +204,7 @@
|
|||||||
#define ONGREEN(signal_id)
|
#define ONGREEN(signal_id)
|
||||||
#define ONRED(signal_id)
|
#define ONRED(signal_id)
|
||||||
#define ONTHROW(turnout_id)
|
#define ONTHROW(turnout_id)
|
||||||
|
#define ONCHANGE(sensor_id)
|
||||||
#define PAUSE
|
#define PAUSE
|
||||||
#define PIN_TURNOUT(id,pin,description...)
|
#define PIN_TURNOUT(id,pin,description...)
|
||||||
#define PRINT(msg)
|
#define PRINT(msg)
|
||||||
|
@ -285,6 +285,7 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = {
|
|||||||
#define IFRESERVE(block) OPCODE_IFRESERVE,V(block),
|
#define IFRESERVE(block) OPCODE_IFRESERVE,V(block),
|
||||||
#define IFTHROWN(turnout_id) OPCODE_IFTHROWN,V(turnout_id),
|
#define IFTHROWN(turnout_id) OPCODE_IFTHROWN,V(turnout_id),
|
||||||
#define IFTIMEOUT OPCODE_IFTIMEOUT,0,0,
|
#define IFTIMEOUT OPCODE_IFTIMEOUT,0,0,
|
||||||
|
#define IFRE(sensor_id,value) OPCODE_IFRE,V(sensor_id),OPCODE_PAD,V(value),
|
||||||
#define INVERT_DIRECTION OPCODE_INVERT_DIRECTION,0,0,
|
#define INVERT_DIRECTION OPCODE_INVERT_DIRECTION,0,0,
|
||||||
#define JOIN OPCODE_JOIN,0,0,
|
#define JOIN OPCODE_JOIN,0,0,
|
||||||
#define KILLALL OPCODE_KILLALL,0,0,
|
#define KILLALL OPCODE_KILLALL,0,0,
|
||||||
@ -301,6 +302,7 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = {
|
|||||||
#define ONGREEN(signal_id) OPCODE_ONGREEN,V(signal_id),
|
#define ONGREEN(signal_id) OPCODE_ONGREEN,V(signal_id),
|
||||||
#define ONRED(signal_id) OPCODE_ONRED,V(signal_id),
|
#define ONRED(signal_id) OPCODE_ONRED,V(signal_id),
|
||||||
#define ONTHROW(turnout_id) OPCODE_ONTHROW,V(turnout_id),
|
#define ONTHROW(turnout_id) OPCODE_ONTHROW,V(turnout_id),
|
||||||
|
#define ONCHANGE(sensor_id) OPCODE_ONCHANGE,V(sensor_id),
|
||||||
#define PAUSE OPCODE_PAUSE,0,0,
|
#define PAUSE OPCODE_PAUSE,0,0,
|
||||||
#define PIN_TURNOUT(id,pin,description...) OPCODE_PINTURNOUT,V(id),OPCODE_PAD,V(pin),
|
#define PIN_TURNOUT(id,pin,description...) OPCODE_PINTURNOUT,V(id),OPCODE_PAD,V(pin),
|
||||||
#define POM(cv,value) OPCODE_POM,V(cv),OPCODE_PAD,V(value),
|
#define POM(cv,value) OPCODE_POM,V(cv),OPCODE_PAD,V(value),
|
||||||
|
127
IO_RotaryEncoder.h
Normal file
127
IO_RotaryEncoder.h
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* © 2022, Peter Cole. All rights reserved.
|
||||||
|
*
|
||||||
|
* This file is part of EX-CommandStation
|
||||||
|
*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The IO_RotaryEncoder device driver is used to receive positions from a rotary encoder connected to an Arduino via I2C.
|
||||||
|
*
|
||||||
|
* There is separate code required for the Arduino the rotary encoder is connected to, which is located here:
|
||||||
|
* https://github.com/peteGSX-Projects/dcc-ex-rotary-encoder
|
||||||
|
*
|
||||||
|
* This device driver receives the rotary encoder position when the rotary encoder button is pushed, and these positions
|
||||||
|
* can be tested in EX-RAIL with:
|
||||||
|
* 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.
|
||||||
|
* A SET(vpin) will flag that a turntable (or anything else) is in motion, and a RESET(vpin) that the motion has finished.
|
||||||
|
*
|
||||||
|
* Refer to the documentation for further information including the valid activities and examples.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef IO_ROTARYENCODER_H
|
||||||
|
#define IO_ROTARYENCODER_H
|
||||||
|
|
||||||
|
#include "EXRAIL2.h"
|
||||||
|
#include "IODevice.h"
|
||||||
|
#include "I2CManager.h"
|
||||||
|
#include "DIAG.h"
|
||||||
|
|
||||||
|
class RotaryEncoder : public IODevice {
|
||||||
|
public:
|
||||||
|
// Constructor
|
||||||
|
RotaryEncoder(VPIN firstVpin, int nPins, uint8_t I2CAddress){
|
||||||
|
_firstVpin = firstVpin;
|
||||||
|
_nPins = nPins;
|
||||||
|
_I2CAddress = I2CAddress;
|
||||||
|
addDevice(this);
|
||||||
|
}
|
||||||
|
static void create(VPIN firstVpin, int nPins, uint8_t I2CAddress) {
|
||||||
|
if (checkNoOverlap(firstVpin, nPins, I2CAddress)) new RotaryEncoder(firstVpin, nPins, I2CAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Initiate the device
|
||||||
|
void _begin() {
|
||||||
|
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);
|
||||||
|
#ifdef DIAG_IO
|
||||||
|
_display();
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
_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 (_position != _previousPosition) {
|
||||||
|
_previousPosition = _position;
|
||||||
|
RMFT2::changeEvent(_firstVpin,1);
|
||||||
|
} else {
|
||||||
|
RMFT2::changeEvent(_firstVpin,0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
delayUntil(currentMicros + 100000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Device specific read function
|
||||||
|
int _readAnalogue(VPIN vpin) override {
|
||||||
|
if (_deviceState == DEVSTATE_FAILED) return 0;
|
||||||
|
return _position;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _write(VPIN vpin, int value) override {
|
||||||
|
if (vpin == _firstVpin + 1) {
|
||||||
|
byte _feedbackBuffer[2] = {RE_OP, value};
|
||||||
|
I2CManager.write(_I2CAddress, _feedbackBuffer, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _display() override {
|
||||||
|
DIAG(F("Rotary Encoder I2C:x%x v%d.%d.%d Configured on Vpin:%d-%d %S"), _I2CAddress, _majorVer, _minorVer, _patchVer,
|
||||||
|
(int)_firstVpin, _firstVpin+_nPins-1, (_deviceState==DEVSTATE_FAILED) ? F("OFFLINE") : F(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t _I2CAddress;
|
||||||
|
int8_t _position;
|
||||||
|
int8_t _previousPosition = 0;
|
||||||
|
uint8_t _versionBuffer[3];
|
||||||
|
uint8_t _buffer[1];
|
||||||
|
uint8_t _majorVer = 0;
|
||||||
|
uint8_t _minorVer = 0;
|
||||||
|
uint8_t _patchVer = 0;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
RE_VER = 0xA0, // Flag to retrieve rotary encoder version from the device
|
||||||
|
RE_OP = 0xA1, // Flag for normal operation
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -194,6 +194,19 @@ void halSetup() {
|
|||||||
//EXIOExpander::create(820, 16, 0x66, 16, 0);
|
//EXIOExpander::create(820, 16, 0x66, 16, 0);
|
||||||
|
|
||||||
|
|
||||||
|
//=======================================================================
|
||||||
|
// The following directive defines a rotary encoder instance.
|
||||||
|
//=======================================================================
|
||||||
|
// The parameters are:
|
||||||
|
// firstVpin = First available Vpin to allocate
|
||||||
|
// numPins= Number of Vpins to allocate, can be either 1 or 2
|
||||||
|
// i2cAddress = Available I2C address (default 0x70)
|
||||||
|
|
||||||
|
//RotaryEncoder::create(firstVpin, numPins, i2cAddress);
|
||||||
|
//RotaryEncoder::create(700, 1, 0x70);
|
||||||
|
//RotaryEncoder::create(701, 2, 0x71);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user