mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-24 16:46:13 +01:00
Compare commits
41 Commits
dca023ffd7
...
a16790f585
Author | SHA1 | Date | |
---|---|---|---|
|
a16790f585 | ||
|
da6a3c442f | ||
|
4fcd81a118 | ||
|
eb450dbd89 | ||
|
a0562fdf5c | ||
|
7ee2c29a52 | ||
|
dba5d35aa2 | ||
|
be10be5a1a | ||
|
bd02d1c15b | ||
|
004d7b6631 | ||
|
21ce87eb3e | ||
|
1f5f7754c1 | ||
|
6adff43f4b | ||
|
152f9850bb | ||
|
3094c52bf8 | ||
|
86f4567556 | ||
|
dd890e65bf | ||
|
1e48c59cd8 | ||
|
004d10ee58 | ||
|
e734661d1b | ||
|
bcb250bacf | ||
|
798241927f | ||
|
df2f09f4d2 | ||
|
f40d57d8bd | ||
|
9fa213e198 | ||
|
a0c1ad182c | ||
|
dbf053858b | ||
|
232ac993ec | ||
|
6cad794411 | ||
|
b0d8510127 | ||
|
3bfdd16288 | ||
|
df4a501e8a | ||
|
2202cb0c5e | ||
|
1425da20b5 | ||
|
b823a647ac | ||
|
57d4655d54 | ||
|
ff9c558b61 | ||
|
b277d204f0 | ||
|
c4febd1d0f | ||
|
98f8022268 | ||
|
1491da4813 |
|
@ -161,6 +161,10 @@ void CommandDistributor::broadcastTurnout(int16_t id, bool isClosed ) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CommandDistributor::broadcastTurntable(int16_t id, uint8_t position, bool moving) {
|
||||||
|
broadcastReply(COMMAND_TYPE, F("<i %d %d %d>\n"), id, position, moving);
|
||||||
|
}
|
||||||
|
|
||||||
void CommandDistributor::broadcastClockTime(int16_t time, int8_t rate) {
|
void CommandDistributor::broadcastClockTime(int16_t time, int8_t rate) {
|
||||||
// The JMRI clock command is of the form : PFT65871<;>4
|
// The JMRI clock command is of the form : PFT65871<;>4
|
||||||
// The CS broadcast is of the form "<jC mmmm nn" where mmmm is time minutes and dd speed
|
// The CS broadcast is of the form "<jC mmmm nn" where mmmm is time minutes and dd speed
|
||||||
|
|
|
@ -49,6 +49,7 @@ public :
|
||||||
static void broadcastLoco(byte slot);
|
static void broadcastLoco(byte slot);
|
||||||
static void broadcastSensor(int16_t id, bool value);
|
static void broadcastSensor(int16_t id, bool value);
|
||||||
static void broadcastTurnout(int16_t id, bool isClosed);
|
static void broadcastTurnout(int16_t id, bool isClosed);
|
||||||
|
static void broadcastTurntable(int16_t id, uint8_t position, bool moving);
|
||||||
static void broadcastClockTime(int16_t time, int8_t rate);
|
static void broadcastClockTime(int16_t time, int8_t rate);
|
||||||
static void setClockTime(int16_t time, int8_t rate, byte opt);
|
static void setClockTime(int16_t time, int8_t rate, byte opt);
|
||||||
static int16_t retClockTime();
|
static int16_t retClockTime();
|
||||||
|
|
158
DCCEXParser.cpp
158
DCCEXParser.cpp
|
@ -114,6 +114,7 @@ Once a new OPCODE is decided upon, update this list.
|
||||||
#include "TrackManager.h"
|
#include "TrackManager.h"
|
||||||
#include "DCCTimer.h"
|
#include "DCCTimer.h"
|
||||||
#include "EXRAIL2.h"
|
#include "EXRAIL2.h"
|
||||||
|
#include "Turntables.h"
|
||||||
|
|
||||||
// This macro can't be created easily as a portable function because the
|
// This macro can't be created easily as a portable function because the
|
||||||
// flashlist requires a far pointer for high flash access.
|
// flashlist requires a far pointer for high flash access.
|
||||||
|
@ -157,6 +158,8 @@ const int16_t HASH_KEYWORD_A='A';
|
||||||
const int16_t HASH_KEYWORD_C='C';
|
const int16_t HASH_KEYWORD_C='C';
|
||||||
const int16_t HASH_KEYWORD_G='G';
|
const int16_t HASH_KEYWORD_G='G';
|
||||||
const int16_t HASH_KEYWORD_I='I';
|
const int16_t HASH_KEYWORD_I='I';
|
||||||
|
const int16_t HASH_KEYWORD_O='O';
|
||||||
|
const int16_t HASH_KEYWORD_P='P';
|
||||||
const int16_t HASH_KEYWORD_R='R';
|
const int16_t HASH_KEYWORD_R='R';
|
||||||
const int16_t HASH_KEYWORD_T='T';
|
const int16_t HASH_KEYWORD_T='T';
|
||||||
const int16_t HASH_KEYWORD_X='X';
|
const int16_t HASH_KEYWORD_X='X';
|
||||||
|
@ -168,6 +171,8 @@ const int16_t HASH_KEYWORD_ANOUT = -26399;
|
||||||
const int16_t HASH_KEYWORD_WIFI = -5583;
|
const int16_t HASH_KEYWORD_WIFI = -5583;
|
||||||
const int16_t HASH_KEYWORD_ETHERNET = -30767;
|
const int16_t HASH_KEYWORD_ETHERNET = -30767;
|
||||||
const int16_t HASH_KEYWORD_WIT = 31594;
|
const int16_t HASH_KEYWORD_WIT = 31594;
|
||||||
|
const int16_t HASH_KEYWORD_EXTT = 8573;
|
||||||
|
const int16_t HASH_KEYWORD_ADD = 3201;
|
||||||
|
|
||||||
int16_t DCCEXParser::stashP[MAX_COMMAND_PARAMS];
|
int16_t DCCEXParser::stashP[MAX_COMMAND_PARAMS];
|
||||||
bool DCCEXParser::stashBusy;
|
bool DCCEXParser::stashBusy;
|
||||||
|
@ -770,11 +775,71 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
||||||
}
|
}
|
||||||
StringFormatter::send(stream, F(">\n"));
|
StringFormatter::send(stream, F(">\n"));
|
||||||
return;
|
return;
|
||||||
|
// No turntables without HAL support
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
case HASH_KEYWORD_O: // <JO returns turntable list
|
||||||
|
StringFormatter::send(stream, F("<jO"));
|
||||||
|
if (params==1) { // <JO>
|
||||||
|
for (Turntable * tto=Turntable::first(); tto; tto=tto->next()) {
|
||||||
|
if (tto->isHidden()) continue;
|
||||||
|
StringFormatter::send(stream, F(" %d"),tto->getId());
|
||||||
|
}
|
||||||
|
StringFormatter::send(stream, F(">\n"));
|
||||||
|
} else { // <JO id>
|
||||||
|
Turntable *tto=Turntable::get(id);
|
||||||
|
if (!tto || tto->isHidden()) {
|
||||||
|
StringFormatter::send(stream, F(" %d X>\n"), id);
|
||||||
|
} else {
|
||||||
|
uint8_t pos = tto->getPosition();
|
||||||
|
uint8_t type = tto->isEXTT();
|
||||||
|
uint8_t posCount = tto->getPositionCount();
|
||||||
|
const FSH *todesc = NULL;
|
||||||
|
#ifdef EXRAIL_ACTIVE
|
||||||
|
todesc = RMFT2::getTurntableDescription(id);
|
||||||
|
#endif
|
||||||
|
if (todesc == NULL) todesc = F("");
|
||||||
|
StringFormatter::send(stream, F(" %d %d %d %d \"%S\">\n"), id, type, pos, posCount, todesc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case HASH_KEYWORD_P: // <JP id> returns turntable position list for the turntable id
|
||||||
|
if (params==2) { // <JP id>
|
||||||
|
Turntable *tto=Turntable::get(id);
|
||||||
|
if (!tto || tto->isHidden()) {
|
||||||
|
StringFormatter::send(stream, F(" %d X>\n"), id);
|
||||||
|
} else {
|
||||||
|
uint8_t posCount = tto->getPositionCount();
|
||||||
|
const FSH *tpdesc = NULL;
|
||||||
|
for (uint8_t p = 0; p < posCount; p++) {
|
||||||
|
StringFormatter::send(stream, F("<jP"));
|
||||||
|
int16_t value = tto->getPositionValue(p);
|
||||||
|
int16_t angle = tto->getPositionAngle(p);
|
||||||
|
#ifdef EXRAIL_ACTIVE
|
||||||
|
tpdesc = RMFT2::getTurntablePositionDescription(id, p);
|
||||||
|
#endif
|
||||||
|
if (tpdesc == NULL) tpdesc = F("");
|
||||||
|
StringFormatter::send(stream, F(" %d %d %d %d \"%S\""), id, p, value, angle, tpdesc);
|
||||||
|
StringFormatter::send(stream, F(">\n"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
StringFormatter::send(stream, F("<jP X>\n"));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
default: break;
|
default: break;
|
||||||
} // switch(p[1])
|
} // switch(p[1])
|
||||||
break; // case J
|
break; // case J
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// No turntables without HAL support
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
case 'I': // TURNTABLE <I ...>
|
||||||
|
if (parseI(stream, params, p))
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
default: //anything else will diagnose and drop out to <X>
|
default: //anything else will diagnose and drop out to <X>
|
||||||
DIAG(F("Opcode=%c params=%d"), opcode, params);
|
DIAG(F("Opcode=%c params=%d"), opcode, params);
|
||||||
for (int i = 0; i < params; i++)
|
for (int i = 0; i < params; i++)
|
||||||
|
@ -1094,6 +1159,99 @@ bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[])
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==========================
|
||||||
|
// Turntable - no support if no HAL
|
||||||
|
// <I> - list all
|
||||||
|
// <I id> - broadcast type and current position
|
||||||
|
// <I id DCC> - create DCC - This is TBA
|
||||||
|
// <I id steps> - operate (DCC)
|
||||||
|
// <I id steps activity> - operate (EXTT)
|
||||||
|
// <I id ADD position value> - add position
|
||||||
|
// <I id EXTT i2caddress vpin home> - create EXTT
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[])
|
||||||
|
{
|
||||||
|
switch (params)
|
||||||
|
{
|
||||||
|
case 0: // <I> list turntable objects
|
||||||
|
return Turntable::printAll(stream);
|
||||||
|
|
||||||
|
case 1: // <I id> broadcast type and current position
|
||||||
|
{
|
||||||
|
Turntable *tto = Turntable::get(p[0]);
|
||||||
|
if (tto) {
|
||||||
|
bool type = tto->isEXTT();
|
||||||
|
uint8_t position = tto->getPosition();
|
||||||
|
StringFormatter::send(stream, F("<i %d %d>\n"), type, position);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case 2: // <I id position> - rotate a DCC turntable
|
||||||
|
{
|
||||||
|
Turntable *tto = Turntable::get(p[0]);
|
||||||
|
if (tto && !tto->isEXTT()) {
|
||||||
|
if (!tto->setPosition(p[0], p[1])) return false;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case 3: // <I id position activity> | <I id DCC home> - rotate to position for EX-Turntable or create DCC turntable
|
||||||
|
{
|
||||||
|
Turntable *tto = Turntable::get(p[0]);
|
||||||
|
if (p[1] == HASH_KEYWORD_DCC) {
|
||||||
|
if (tto || p[2] < 0 || p[2] > 3600) return false;
|
||||||
|
if (!DCCTurntable::create(p[0])) return false;
|
||||||
|
Turntable *tto = Turntable::get(p[0]);
|
||||||
|
tto->addPosition(0, 0, p[2]);
|
||||||
|
StringFormatter::send(stream, F("<i>\n"));
|
||||||
|
} else {
|
||||||
|
if (!tto) return false;
|
||||||
|
if (!tto->isEXTT()) return false;
|
||||||
|
if (!tto->setPosition(p[0], p[1], p[2])) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case 4: // <I id EXTT vpin home> create an EXTT turntable
|
||||||
|
{
|
||||||
|
Turntable *tto = Turntable::get(p[0]);
|
||||||
|
if (p[1] == HASH_KEYWORD_EXTT) {
|
||||||
|
if (tto || p[3] < 0 || p[3] > 3600) return false;
|
||||||
|
if (!EXTTTurntable::create(p[0], (VPIN)p[2])) return false;
|
||||||
|
Turntable *tto = Turntable::get(p[0]);
|
||||||
|
tto->addPosition(0, 0, p[3]);
|
||||||
|
StringFormatter::send(stream, F("<i>\n"));
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case 5: // <I id ADD position value angle> add a position
|
||||||
|
{
|
||||||
|
Turntable *tto = Turntable::get(p[0]);
|
||||||
|
if (p[1] == HASH_KEYWORD_ADD) {
|
||||||
|
// tto must exist, no more than 48 positions, angle 0 - 3600
|
||||||
|
if (!tto || p[2] > 48 || p[4] < 0 || p[4] > 3600) return false;
|
||||||
|
tto->addPosition(p[2], p[3], p[4]);
|
||||||
|
StringFormatter::send(stream, F("<i>\n"));
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default: // Anything else is invalid
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// CALLBACKS must be static
|
// CALLBACKS must be static
|
||||||
bool DCCEXParser::stashCallback(Print *stream, int16_t p[MAX_COMMAND_PARAMS], RingStream * ringStream)
|
bool DCCEXParser::stashCallback(Print *stream, int16_t p[MAX_COMMAND_PARAMS], RingStream * ringStream)
|
||||||
{
|
{
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "FSH.h"
|
#include "FSH.h"
|
||||||
#include "RingStream.h"
|
#include "RingStream.h"
|
||||||
|
#include "defines.h"
|
||||||
|
|
||||||
typedef void (*FILTER_CALLBACK)(Print * stream, byte & opcode, byte & paramCount, int16_t p[]);
|
typedef void (*FILTER_CALLBACK)(Print * stream, byte & opcode, byte & paramCount, int16_t p[]);
|
||||||
typedef void (*AT_COMMAND_CALLBACK)(HardwareSerial * stream,const byte * command);
|
typedef void (*AT_COMMAND_CALLBACK)(HardwareSerial * stream,const byte * command);
|
||||||
|
@ -45,13 +46,16 @@ struct DCCEXParser
|
||||||
static int16_t splitValues( int16_t result[MAX_COMMAND_PARAMS], const byte * command, bool usehex);
|
static int16_t splitValues( int16_t result[MAX_COMMAND_PARAMS], const byte * command, bool usehex);
|
||||||
|
|
||||||
static bool parseT(Print * stream, int16_t params, int16_t p[]);
|
static bool parseT(Print * stream, int16_t params, int16_t p[]);
|
||||||
static bool parseZ(Print * stream, int16_t params, int16_t p[]);
|
static bool parseZ(Print * stream, int16_t params, int16_t p[]);
|
||||||
static bool parseS(Print * stream, int16_t params, int16_t p[]);
|
static bool parseS(Print * stream, int16_t params, int16_t p[]);
|
||||||
static bool parsef(Print * stream, int16_t params, int16_t p[]);
|
static bool parsef(Print * stream, int16_t params, int16_t p[]);
|
||||||
static bool parseD(Print * stream, int16_t params, int16_t p[]);
|
static bool parseD(Print * stream, int16_t params, int16_t p[]);
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
static bool parseI(Print * stream, int16_t params, int16_t p[]);
|
||||||
|
#endif
|
||||||
|
|
||||||
static Print * getAsyncReplyStream();
|
static Print * getAsyncReplyStream();
|
||||||
static void commitAsyncReplyStream();
|
static void commitAsyncReplyStream();
|
||||||
|
|
||||||
static bool stashBusy;
|
static bool stashBusy;
|
||||||
static byte stashTarget;
|
static byte stashTarget;
|
||||||
|
|
88
EXRAIL2.cpp
88
EXRAIL2.cpp
|
@ -52,6 +52,8 @@
|
||||||
#include "Turnouts.h"
|
#include "Turnouts.h"
|
||||||
#include "CommandDistributor.h"
|
#include "CommandDistributor.h"
|
||||||
#include "TrackManager.h"
|
#include "TrackManager.h"
|
||||||
|
#include "Turntables.h"
|
||||||
|
#include "IODevice.h"
|
||||||
|
|
||||||
// Command parsing keywords
|
// Command parsing keywords
|
||||||
const int16_t HASH_KEYWORD_EXRAIL=15435;
|
const int16_t HASH_KEYWORD_EXRAIL=15435;
|
||||||
|
@ -94,6 +96,9 @@ LookList * RMFT2::onAmberLookup=NULL;
|
||||||
LookList * RMFT2::onGreenLookup=NULL;
|
LookList * RMFT2::onGreenLookup=NULL;
|
||||||
LookList * RMFT2::onChangeLookup=NULL;
|
LookList * RMFT2::onChangeLookup=NULL;
|
||||||
LookList * RMFT2::onClockLookup=NULL;
|
LookList * RMFT2::onClockLookup=NULL;
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
LookList * RMFT2::onRotateLookup=NULL;
|
||||||
|
#endif
|
||||||
LookList * RMFT2::onOverloadLookup=NULL;
|
LookList * RMFT2::onOverloadLookup=NULL;
|
||||||
|
|
||||||
#define GET_OPCODE GETHIGHFLASH(RMFT2::RouteCode,progCounter)
|
#define GET_OPCODE GETHIGHFLASH(RMFT2::RouteCode,progCounter)
|
||||||
|
@ -176,9 +181,11 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) {
|
||||||
onGreenLookup=LookListLoader(OPCODE_ONGREEN);
|
onGreenLookup=LookListLoader(OPCODE_ONGREEN);
|
||||||
onChangeLookup=LookListLoader(OPCODE_ONCHANGE);
|
onChangeLookup=LookListLoader(OPCODE_ONCHANGE);
|
||||||
onClockLookup=LookListLoader(OPCODE_ONTIME);
|
onClockLookup=LookListLoader(OPCODE_ONTIME);
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
onRotateLookup=LookListLoader(OPCODE_ONROTATE);
|
||||||
|
#endif
|
||||||
onOverloadLookup=LookListLoader(OPCODE_ONOVERLOAD);
|
onOverloadLookup=LookListLoader(OPCODE_ONOVERLOAD);
|
||||||
|
|
||||||
|
|
||||||
// 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
|
||||||
for (int sigslot=0;;sigslot++) {
|
for (int sigslot=0;;sigslot++) {
|
||||||
|
@ -241,7 +248,38 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) {
|
||||||
setTurnoutHiddenState(VpinTurnout::create(id,pin));
|
setTurnoutHiddenState(VpinTurnout::create(id,pin));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
case OPCODE_DCCTURNTABLE: {
|
||||||
|
VPIN id=operand;
|
||||||
|
int home=getOperand(progCounter,1);
|
||||||
|
setTurntableHiddenState(DCCTurntable::create(id));
|
||||||
|
Turntable *tto=Turntable::get(id);
|
||||||
|
tto->addPosition(0,0,home);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OPCODE_EXTTTURNTABLE: {
|
||||||
|
VPIN id=operand;
|
||||||
|
VPIN pin=getOperand(progCounter,1);
|
||||||
|
int home=getOperand(progCounter,3);
|
||||||
|
setTurntableHiddenState(EXTTTurntable::create(id,pin));
|
||||||
|
Turntable *tto=Turntable::get(id);
|
||||||
|
tto->addPosition(0,0,home);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OPCODE_TTADDPOSITION: {
|
||||||
|
VPIN id=operand;
|
||||||
|
int position=getOperand(progCounter,1);
|
||||||
|
int value=getOperand(progCounter,2);
|
||||||
|
int angle=getOperand(progCounter,3);
|
||||||
|
Turntable *tto=Turntable::get(id);
|
||||||
|
tto->addPosition(position,value,angle);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
case OPCODE_AUTOSTART:
|
case OPCODE_AUTOSTART:
|
||||||
// automatically create a task from here at startup.
|
// automatically create a task from here at startup.
|
||||||
// Removed if (progCounter>0) check 4.2.31 because
|
// Removed if (progCounter>0) check 4.2.31 because
|
||||||
|
@ -266,6 +304,12 @@ void RMFT2::setTurnoutHiddenState(Turnout * t) {
|
||||||
t->setHidden(GETFLASH(getTurnoutDescription(t->getId()))==0x01);
|
t->setHidden(GETFLASH(getTurnoutDescription(t->getId()))==0x01);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
void RMFT2::setTurntableHiddenState(Turntable * tto) {
|
||||||
|
tto->setHidden(GETFLASH(getTurntableDescription(tto->getId()))==0x01);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
char RMFT2::getRouteType(int16_t id) {
|
char RMFT2::getRouteType(int16_t id) {
|
||||||
for (int16_t i=0;;i+=2) {
|
for (int16_t i=0;;i+=2) {
|
||||||
int16_t rid= GETHIGHFLASHW(routeIdList,i);
|
int16_t rid= GETHIGHFLASHW(routeIdList,i);
|
||||||
|
@ -602,6 +646,14 @@ void RMFT2::loop2() {
|
||||||
Turnout::setClosed(operand, true);
|
Turnout::setClosed(operand, true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
case OPCODE_ROTATE:
|
||||||
|
uint8_t activity;
|
||||||
|
activity=getOperand(2);
|
||||||
|
Turntable::setPosition(operand,getOperand(1),activity);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
case OPCODE_REV:
|
case OPCODE_REV:
|
||||||
forward = false;
|
forward = false;
|
||||||
driveLoco(operand);
|
driveLoco(operand);
|
||||||
|
@ -801,7 +853,13 @@ void RMFT2::loop2() {
|
||||||
case OPCODE_IFCLOSED:
|
case OPCODE_IFCLOSED:
|
||||||
skipIf=Turnout::isThrown(operand);
|
skipIf=Turnout::isThrown(operand);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
case OPCODE_IFTTPOSITION: // do block if turntable at this position
|
||||||
|
skipIf=Turntable::getPosition(operand)!=(int)getOperand(1);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
case OPCODE_ENDIF:
|
case OPCODE_ENDIF:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -974,7 +1032,16 @@ void RMFT2::loop2() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
case OPCODE_WAITFORTT: // OPCODE_WAITFOR,V(turntable_id)
|
||||||
|
if (Turntable::ttMoving(operand)) {
|
||||||
|
delayMe(100);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
case OPCODE_PRINT:
|
case OPCODE_PRINT:
|
||||||
printMessage(operand);
|
printMessage(operand);
|
||||||
break;
|
break;
|
||||||
|
@ -999,6 +1066,12 @@ void RMFT2::loop2() {
|
||||||
case OPCODE_ONGREEN:
|
case OPCODE_ONGREEN:
|
||||||
case OPCODE_ONCHANGE:
|
case OPCODE_ONCHANGE:
|
||||||
case OPCODE_ONTIME:
|
case OPCODE_ONTIME:
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
case OPCODE_DCCTURNTABLE: // Turntable definition ignored at runtime
|
||||||
|
case OPCODE_EXTTTURNTABLE: // Turntable definition ignored at runtime
|
||||||
|
case OPCODE_TTADDPOSITION: // Turntable position definition ignored at runtime
|
||||||
|
case OPCODE_ONROTATE:
|
||||||
|
#endif
|
||||||
case OPCODE_ONOVERLOAD:
|
case OPCODE_ONOVERLOAD:
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -1144,6 +1217,13 @@ void RMFT2::changeEvent(int16_t vpin, bool change) {
|
||||||
if (change) handleEvent(F("CHANGE"),onChangeLookup,vpin);
|
if (change) handleEvent(F("CHANGE"),onChangeLookup,vpin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
void RMFT2::rotateEvent(int16_t turntableId, bool change) {
|
||||||
|
// Hunt or an ONROTATE for this turntable
|
||||||
|
if (change) handleEvent(F("ROTATE"),onRotateLookup,turntableId);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void RMFT2::clockEvent(int16_t clocktime, bool change) {
|
void RMFT2::clockEvent(int16_t clocktime, bool change) {
|
||||||
// Hunt for an ONTIME for this time
|
// Hunt for an ONTIME for this time
|
||||||
if (Diag::CMD)
|
if (Diag::CMD)
|
||||||
|
|
14
EXRAIL2.h
14
EXRAIL2.h
|
@ -25,6 +25,7 @@
|
||||||
#include "FSH.h"
|
#include "FSH.h"
|
||||||
#include "IODevice.h"
|
#include "IODevice.h"
|
||||||
#include "Turnouts.h"
|
#include "Turnouts.h"
|
||||||
|
#include "Turntables.h"
|
||||||
|
|
||||||
// The following are the operation codes (or instructions) for a kind of virtual machine.
|
// The following are the operation codes (or instructions) for a kind of virtual machine.
|
||||||
// Each instruction is normally 3 bytes long with an operation code followed by a parameter.
|
// Each instruction is normally 3 bytes long with an operation code followed by a parameter.
|
||||||
|
@ -63,6 +64,10 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,
|
||||||
OPCODE_ONCHANGE,
|
OPCODE_ONCHANGE,
|
||||||
OPCODE_ONCLOCKTIME,
|
OPCODE_ONCLOCKTIME,
|
||||||
OPCODE_ONTIME,
|
OPCODE_ONTIME,
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
OPCODE_TTADDPOSITION,OPCODE_DCCTURNTABLE,OPCODE_EXTTTURNTABLE,
|
||||||
|
OPCODE_ONROTATE,OPCODE_ROTATE,OPCODE_IFTTPOSITION,OPCODE_WAITFORTT,
|
||||||
|
#endif
|
||||||
OPCODE_ONOVERLOAD,
|
OPCODE_ONOVERLOAD,
|
||||||
|
|
||||||
// OPcodes below this point are skip-nesting IF operations
|
// OPcodes below this point are skip-nesting IF operations
|
||||||
|
@ -132,6 +137,7 @@ class LookList {
|
||||||
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 void changeEvent(int16_t id, bool change);
|
||||||
static void clockEvent(int16_t clocktime, bool change);
|
static void clockEvent(int16_t clocktime, bool change);
|
||||||
|
static void rotateEvent(int16_t id, bool change);
|
||||||
static void powerEvent(int16_t track, bool overload);
|
static void powerEvent(int16_t track, bool overload);
|
||||||
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;
|
||||||
|
@ -147,6 +153,8 @@ class LookList {
|
||||||
static const FSH * getTurnoutDescription(int16_t id);
|
static const FSH * getTurnoutDescription(int16_t id);
|
||||||
static const FSH * getRosterName(int16_t id);
|
static const FSH * getRosterName(int16_t id);
|
||||||
static const FSH * getRosterFunctions(int16_t id);
|
static const FSH * getRosterFunctions(int16_t id);
|
||||||
|
static const FSH * getTurntableDescription(int16_t id);
|
||||||
|
static const FSH * getTurntablePositionDescription(int16_t turntableId, uint8_t positionId);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16_t p[]);
|
static void ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16_t p[]);
|
||||||
|
@ -159,6 +167,9 @@ private:
|
||||||
static bool isSignal(int16_t id,char rag);
|
static bool isSignal(int16_t id,char rag);
|
||||||
static int16_t getSignalSlot(int16_t id);
|
static int16_t getSignalSlot(int16_t id);
|
||||||
static void setTurnoutHiddenState(Turnout * t);
|
static void setTurnoutHiddenState(Turnout * t);
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
static void setTurntableHiddenState(Turntable * tto);
|
||||||
|
#endif
|
||||||
static LookList* LookListLoader(OPCODE op1,
|
static LookList* LookListLoader(OPCODE op1,
|
||||||
OPCODE op2=OPCODE_ENDEXRAIL,OPCODE op3=OPCODE_ENDEXRAIL);
|
OPCODE op2=OPCODE_ENDEXRAIL,OPCODE op3=OPCODE_ENDEXRAIL);
|
||||||
static void handleEvent(const FSH* reason,LookList* handlers, int16_t id);
|
static void handleEvent(const FSH* reason,LookList* handlers, int16_t id);
|
||||||
|
@ -191,6 +202,9 @@ private:
|
||||||
static LookList * onGreenLookup;
|
static LookList * onGreenLookup;
|
||||||
static LookList * onChangeLookup;
|
static LookList * onChangeLookup;
|
||||||
static LookList * onClockLookup;
|
static LookList * onClockLookup;
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
static LookList * onRotateLookup;
|
||||||
|
#endif
|
||||||
static LookList * onOverloadLookup;
|
static LookList * onOverloadLookup;
|
||||||
|
|
||||||
// Local variables - exist for each instance/task
|
// Local variables - exist for each instance/task
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#undef CALL
|
#undef CALL
|
||||||
#undef CLOSE
|
#undef CLOSE
|
||||||
#undef DCC_SIGNAL
|
#undef DCC_SIGNAL
|
||||||
|
#undef DCC_TURNTABLE
|
||||||
#undef DEACTIVATE
|
#undef DEACTIVATE
|
||||||
#undef DEACTIVATEL
|
#undef DEACTIVATEL
|
||||||
#undef DELAY
|
#undef DELAY
|
||||||
|
@ -52,8 +53,9 @@
|
||||||
#undef ENDEXRAIL
|
#undef ENDEXRAIL
|
||||||
#undef ENDIF
|
#undef ENDIF
|
||||||
#undef ENDTASK
|
#undef ENDTASK
|
||||||
#undef ESTOP
|
#undef ESTOP
|
||||||
#undef EXRAIL
|
#undef EXRAIL
|
||||||
|
#undef EXTT_TURNTABLE
|
||||||
#undef FADE
|
#undef FADE
|
||||||
#undef FOFF
|
#undef FOFF
|
||||||
#undef FOLLOW
|
#undef FOLLOW
|
||||||
|
@ -76,6 +78,7 @@
|
||||||
#undef IFRESERVE
|
#undef IFRESERVE
|
||||||
#undef IFTHROWN
|
#undef IFTHROWN
|
||||||
#undef IFTIMEOUT
|
#undef IFTIMEOUT
|
||||||
|
#undef IFTTPOSITION
|
||||||
#undef IFRE
|
#undef IFRE
|
||||||
#undef INVERT_DIRECTION
|
#undef INVERT_DIRECTION
|
||||||
#undef JOIN
|
#undef JOIN
|
||||||
|
@ -97,6 +100,7 @@
|
||||||
#undef ONOVERLOAD
|
#undef ONOVERLOAD
|
||||||
#undef ONGREEN
|
#undef ONGREEN
|
||||||
#undef ONRED
|
#undef ONRED
|
||||||
|
#undef ONROTATE
|
||||||
#undef ONTHROW
|
#undef ONTHROW
|
||||||
#undef ONCHANGE
|
#undef ONCHANGE
|
||||||
#undef PARSE
|
#undef PARSE
|
||||||
|
@ -115,7 +119,9 @@
|
||||||
#undef RESUME
|
#undef RESUME
|
||||||
#undef RETURN
|
#undef RETURN
|
||||||
#undef REV
|
#undef REV
|
||||||
#undef ROSTER
|
#undef ROSTER
|
||||||
|
#undef ROTATE
|
||||||
|
#undef ROTATE_DCC
|
||||||
#undef ROUTE
|
#undef ROUTE
|
||||||
#undef SENDLOCO
|
#undef SENDLOCO
|
||||||
#undef SEQUENCE
|
#undef SEQUENCE
|
||||||
|
@ -138,7 +144,8 @@
|
||||||
#undef SPEED
|
#undef SPEED
|
||||||
#undef START
|
#undef START
|
||||||
#undef STOP
|
#undef STOP
|
||||||
#undef THROW
|
#undef THROW
|
||||||
|
#undef TT_ADDPOSITION
|
||||||
#undef TURNOUT
|
#undef TURNOUT
|
||||||
#undef TURNOUTL
|
#undef TURNOUTL
|
||||||
#undef UNJOIN
|
#undef UNJOIN
|
||||||
|
@ -146,6 +153,9 @@
|
||||||
#undef VIRTUAL_SIGNAL
|
#undef VIRTUAL_SIGNAL
|
||||||
#undef VIRTUAL_TURNOUT
|
#undef VIRTUAL_TURNOUT
|
||||||
#undef WAITFOR
|
#undef WAITFOR
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
#undef WAITFORTT
|
||||||
|
#endif
|
||||||
#undef WITHROTTLE
|
#undef WITHROTTLE
|
||||||
#undef XFOFF
|
#undef XFOFF
|
||||||
#undef XFON
|
#undef XFON
|
||||||
|
@ -168,6 +178,7 @@
|
||||||
#define CALL(route)
|
#define CALL(route)
|
||||||
#define CLOSE(id)
|
#define CLOSE(id)
|
||||||
#define DCC_SIGNAL(id,add,subaddr)
|
#define DCC_SIGNAL(id,add,subaddr)
|
||||||
|
#define DCC_TURNTABLE(id,home,description)
|
||||||
#define DEACTIVATE(addr,subaddr)
|
#define DEACTIVATE(addr,subaddr)
|
||||||
#define DEACTIVATEL(addr)
|
#define DEACTIVATEL(addr)
|
||||||
#define DELAY(mindelay)
|
#define DELAY(mindelay)
|
||||||
|
@ -180,7 +191,8 @@
|
||||||
#define ENDIF
|
#define ENDIF
|
||||||
#define ENDTASK
|
#define ENDTASK
|
||||||
#define ESTOP
|
#define ESTOP
|
||||||
#define EXRAIL
|
#define EXRAIL
|
||||||
|
#define EXTT_TURNTABLE(id,vpin,i2c_address,home,description)
|
||||||
#define FADE(pin,value,ms)
|
#define FADE(pin,value,ms)
|
||||||
#define FOFF(func)
|
#define FOFF(func)
|
||||||
#define FOLLOW(route)
|
#define FOLLOW(route)
|
||||||
|
@ -203,6 +215,7 @@
|
||||||
#define IFTHROWN(turnout_id)
|
#define IFTHROWN(turnout_id)
|
||||||
#define IFRESERVE(block)
|
#define IFRESERVE(block)
|
||||||
#define IFTIMEOUT
|
#define IFTIMEOUT
|
||||||
|
#define IFTTPOSITION(turntable_id,position)
|
||||||
#define IFRE(sensor_id,value)
|
#define IFRE(sensor_id,value)
|
||||||
#define INVERT_DIRECTION
|
#define INVERT_DIRECTION
|
||||||
#define JOIN
|
#define JOIN
|
||||||
|
@ -223,7 +236,8 @@
|
||||||
#define ONDEACTIVATEL(linear)
|
#define ONDEACTIVATEL(linear)
|
||||||
#define ONCLOSE(turnout_id)
|
#define ONCLOSE(turnout_id)
|
||||||
#define ONGREEN(signal_id)
|
#define ONGREEN(signal_id)
|
||||||
#define ONRED(signal_id)
|
#define ONRED(signal_id)
|
||||||
|
#define ONROTATE(turntable_id)
|
||||||
#define ONTHROW(turnout_id)
|
#define ONTHROW(turnout_id)
|
||||||
#define ONCHANGE(sensor_id)
|
#define ONCHANGE(sensor_id)
|
||||||
#define PAUSE
|
#define PAUSE
|
||||||
|
@ -242,8 +256,10 @@
|
||||||
#define RESUME
|
#define RESUME
|
||||||
#define RETURN
|
#define RETURN
|
||||||
#define REV(speed)
|
#define REV(speed)
|
||||||
#define ROUTE(id,description)
|
#define ROTATE(turntable_id,position,activity)
|
||||||
|
#define ROTATE_DCC(turntable_id,position)
|
||||||
#define ROSTER(cab,name,funcmap...)
|
#define ROSTER(cab,name,funcmap...)
|
||||||
|
#define ROUTE(id,description)
|
||||||
#define SENDLOCO(cab,route)
|
#define SENDLOCO(cab,route)
|
||||||
#define SEQUENCE(id)
|
#define SEQUENCE(id)
|
||||||
#define SERIAL(msg)
|
#define SERIAL(msg)
|
||||||
|
@ -265,7 +281,8 @@
|
||||||
#define SPEED(speed)
|
#define SPEED(speed)
|
||||||
#define START(route)
|
#define START(route)
|
||||||
#define STOP
|
#define STOP
|
||||||
#define THROW(id)
|
#define THROW(id)
|
||||||
|
#define TT_ADDPOSITION(turntable_id,position,value,angle,description...)
|
||||||
#define TURNOUT(id,addr,subaddr,description...)
|
#define TURNOUT(id,addr,subaddr,description...)
|
||||||
#define TURNOUTL(id,addr,description...)
|
#define TURNOUTL(id,addr,description...)
|
||||||
#define UNJOIN
|
#define UNJOIN
|
||||||
|
@ -273,6 +290,9 @@
|
||||||
#define VIRTUAL_SIGNAL(id)
|
#define VIRTUAL_SIGNAL(id)
|
||||||
#define VIRTUAL_TURNOUT(id,description...)
|
#define VIRTUAL_TURNOUT(id,description...)
|
||||||
#define WAITFOR(pin)
|
#define WAITFOR(pin)
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
#define WAITFORTT(turntable_id)
|
||||||
|
#endif
|
||||||
#define WITHROTTLE(msg)
|
#define WITHROTTLE(msg)
|
||||||
#define XFOFF(cab,func)
|
#define XFOFF(cab,func)
|
||||||
#define XFON(cab,func)
|
#define XFON(cab,func)
|
||||||
|
|
|
@ -54,6 +54,8 @@
|
||||||
|
|
||||||
// helper macro for turnout descriptions, creates NULL for missing description
|
// helper macro for turnout descriptions, creates NULL for missing description
|
||||||
#define O_DESC(id, desc) case id: return ("" desc)[0]?F("" desc):NULL;
|
#define O_DESC(id, desc) case id: return ("" desc)[0]?F("" desc):NULL;
|
||||||
|
// helper macro for turntable descriptions, creates NULL for missing description
|
||||||
|
#define T_DESC(tid,pid,desc) if(turntableId==tid && positionId==pid) return ("" desc)[0]?F("" desc):NULL;
|
||||||
// helper macro for turnout description as HIDDEN
|
// helper macro for turnout description as HIDDEN
|
||||||
#define HIDDEN "\x01"
|
#define HIDDEN "\x01"
|
||||||
|
|
||||||
|
@ -67,10 +69,13 @@
|
||||||
#define ALIAS(name,value...) const int name= 1##value##0 ==10 ? -__COUNTER__ : value##0/10;
|
#define ALIAS(name,value...) const int name= 1##value##0 ==10 ? -__COUNTER__ : value##0/10;
|
||||||
#include "myAutomation.h"
|
#include "myAutomation.h"
|
||||||
|
|
||||||
// Pass 1h Implements HAL macro by creating exrailHalSetup function
|
// Pass 1h Implements HAL macro by creating exrailHalSetup function
|
||||||
|
// Also allows creating EXTurntable object
|
||||||
#include "EXRAIL2MacroReset.h"
|
#include "EXRAIL2MacroReset.h"
|
||||||
#undef HAL
|
#undef HAL
|
||||||
#define HAL(haltype,params...) haltype::create(params);
|
#define HAL(haltype,params...) haltype::create(params);
|
||||||
|
#undef EXTT_TURNTABLE
|
||||||
|
#define EXTT_TURNTABLE(id,vpin,i2c_address,home,description...) EXTurntable::create(vpin,1,i2c_address);
|
||||||
void exrailHalSetup() {
|
void exrailHalSetup() {
|
||||||
#include "myAutomation.h"
|
#include "myAutomation.h"
|
||||||
}
|
}
|
||||||
|
@ -187,6 +192,31 @@ const FSH * RMFT2::getTurnoutDescription(int16_t turnoutid) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pass to get turntable descriptions (optional)
|
||||||
|
#include "EXRAIL2MacroReset.h"
|
||||||
|
#undef DCC_TURNTABLE
|
||||||
|
#define DCC_TURNTABLE(id,home,description...) O_DESC(id,description)
|
||||||
|
#undef EXTT_TURNTABLE
|
||||||
|
#define EXTT_TURNTABLE(id,vpin,i2c_address,home,description...) O_DESC(id,description)
|
||||||
|
|
||||||
|
const FSH * RMFT2::getTurntableDescription(int16_t turntableId) {
|
||||||
|
switch (turntableId) {
|
||||||
|
#include "myAutomation.h"
|
||||||
|
default:break;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass to get turntable position descriptions (optional)
|
||||||
|
#include "EXRAIL2MacroReset.h"
|
||||||
|
#undef TT_ADDPOSITION
|
||||||
|
#define TT_ADDPOSITION(turntable_id,position,value,home,description...) T_DESC(turntable_id,position,description)
|
||||||
|
|
||||||
|
const FSH * RMFT2::getTurntablePositionDescription(int16_t turntableId, uint8_t positionId) {
|
||||||
|
#include "myAutomation.h"
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
// Pass 6: Roster IDs (count)
|
// Pass 6: Roster IDs (count)
|
||||||
#include "EXRAIL2MacroReset.h"
|
#include "EXRAIL2MacroReset.h"
|
||||||
#undef ROSTER
|
#undef ROSTER
|
||||||
|
@ -269,6 +299,9 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = {
|
||||||
#define BROADCAST(msg) PRINT(msg)
|
#define BROADCAST(msg) PRINT(msg)
|
||||||
#define CALL(route) OPCODE_CALL,V(route),
|
#define CALL(route) OPCODE_CALL,V(route),
|
||||||
#define CLOSE(id) OPCODE_CLOSE,V(id),
|
#define CLOSE(id) OPCODE_CLOSE,V(id),
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
#define DCC_TURNTABLE(id,home,description...) OPCODE_DCCTURNTABLE,V(id),OPCODE_PAD,V(home),
|
||||||
|
#endif
|
||||||
#define DEACTIVATE(addr,subaddr) OPCODE_DCCACTIVATE,V(addr<<3 | subaddr<<1),
|
#define DEACTIVATE(addr,subaddr) OPCODE_DCCACTIVATE,V(addr<<3 | subaddr<<1),
|
||||||
#define DEACTIVATEL(addr) OPCODE_DCCACTIVATE,V((addr+3)<<1),
|
#define DEACTIVATEL(addr) OPCODE_DCCACTIVATE,V((addr+3)<<1),
|
||||||
#define DELAY(ms) ms<30000?OPCODE_DELAYMS:OPCODE_DELAY,V(ms/(ms<30000?1L:100L)),
|
#define DELAY(ms) ms<30000?OPCODE_DELAYMS:OPCODE_DELAY,V(ms/(ms<30000?1L:100L)),
|
||||||
|
@ -282,7 +315,10 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = {
|
||||||
#define ENDIF OPCODE_ENDIF,0,0,
|
#define ENDIF OPCODE_ENDIF,0,0,
|
||||||
#define ENDTASK OPCODE_ENDTASK,0,0,
|
#define ENDTASK OPCODE_ENDTASK,0,0,
|
||||||
#define ESTOP OPCODE_SPEED,V(1),
|
#define ESTOP OPCODE_SPEED,V(1),
|
||||||
#define EXRAIL
|
#define EXRAIL
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
#define EXTT_TURNTABLE(id,vpin,i2c_address,home,description...) OPCODE_EXTTTURNTABLE,V(id),OPCODE_PAD,V(vpin),OPCODE_PAD,V(i2c_address),OPCODE_PAD,V(home),
|
||||||
|
#endif
|
||||||
#define FADE(pin,value,ms) OPCODE_SERVO,V(pin),OPCODE_PAD,V(value),OPCODE_PAD,V(PCA9685::ProfileType::UseDuration|PCA9685::NoPowerOff),OPCODE_PAD,V(ms/100L),
|
#define FADE(pin,value,ms) OPCODE_SERVO,V(pin),OPCODE_PAD,V(value),OPCODE_PAD,V(PCA9685::ProfileType::UseDuration|PCA9685::NoPowerOff),OPCODE_PAD,V(ms/100L),
|
||||||
#define FOFF(func) OPCODE_FOFF,V(func),
|
#define FOFF(func) OPCODE_FOFF,V(func),
|
||||||
#define FOLLOW(route) OPCODE_FOLLOW,V(route),
|
#define FOLLOW(route) OPCODE_FOLLOW,V(route),
|
||||||
|
@ -305,6 +341,9 @@ 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,
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
#define IFTTPOSITION(id,position) OPCODE_IFTTPOSITION,V(id),OPCODE_PAD,V(position),
|
||||||
|
#endif
|
||||||
#define IFRE(sensor_id,value) OPCODE_IFRE,V(sensor_id),OPCODE_PAD,V(value),
|
#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,
|
||||||
|
@ -326,6 +365,9 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = {
|
||||||
#define ONDEACTIVATEL(linear) OPCODE_ONDEACTIVATE,V(linear+3),
|
#define ONDEACTIVATEL(linear) OPCODE_ONDEACTIVATE,V(linear+3),
|
||||||
#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),
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
#define ONROTATE(id) OPCODE_ONROTATE,V(id),
|
||||||
|
#endif
|
||||||
#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 ONCHANGE(sensor_id) OPCODE_ONCHANGE,V(sensor_id),
|
||||||
#define PAUSE OPCODE_PAUSE,0,0,
|
#define PAUSE OPCODE_PAUSE,0,0,
|
||||||
|
@ -345,6 +387,10 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = {
|
||||||
#define RETURN OPCODE_RETURN,0,0,
|
#define RETURN OPCODE_RETURN,0,0,
|
||||||
#define REV(speed) OPCODE_REV,V(speed),
|
#define REV(speed) OPCODE_REV,V(speed),
|
||||||
#define ROSTER(cabid,name,funcmap...)
|
#define ROSTER(cabid,name,funcmap...)
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
#define ROTATE(id,position,activity) OPCODE_ROTATE,V(id),OPCODE_PAD,V(position),OPCODE_PAD,V(EXTurntable::activity),
|
||||||
|
#define ROTATE_DCC(id,position) OPCODE_ROTATE,V(id),OPCODE_PAD,V(position),OPCODE_PAD,V(0),
|
||||||
|
#endif
|
||||||
#define ROUTE(id, description) OPCODE_ROUTE, V(id),
|
#define ROUTE(id, description) OPCODE_ROUTE, V(id),
|
||||||
#define SENDLOCO(cab,route) OPCODE_SENDLOCO,V(cab),OPCODE_PAD,V(route),
|
#define SENDLOCO(cab,route) OPCODE_SENDLOCO,V(cab),OPCODE_PAD,V(route),
|
||||||
#define SEQUENCE(id) OPCODE_SEQUENCE, V(id),
|
#define SEQUENCE(id) OPCODE_SEQUENCE, V(id),
|
||||||
|
@ -368,6 +414,9 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = {
|
||||||
#define START(route) OPCODE_START,V(route),
|
#define START(route) OPCODE_START,V(route),
|
||||||
#define STOP OPCODE_SPEED,V(0),
|
#define STOP OPCODE_SPEED,V(0),
|
||||||
#define THROW(id) OPCODE_THROW,V(id),
|
#define THROW(id) OPCODE_THROW,V(id),
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
#define TT_ADDPOSITION(id,position,value,angle,description...) OPCODE_TTADDPOSITION,V(id),OPCODE_PAD,V(position),OPCODE_PAD,V(value),OPCODE_PAD,V(angle),
|
||||||
|
#endif
|
||||||
#define TURNOUT(id,addr,subaddr,description...) OPCODE_TURNOUT,V(id),OPCODE_PAD,V(addr),OPCODE_PAD,V(subaddr),
|
#define TURNOUT(id,addr,subaddr,description...) OPCODE_TURNOUT,V(id),OPCODE_PAD,V(addr),OPCODE_PAD,V(subaddr),
|
||||||
#define TURNOUTL(id,addr,description...) TURNOUT(id,(addr-1)/4+1,(addr-1)%4, description)
|
#define TURNOUTL(id,addr,description...) TURNOUT(id,(addr-1)/4+1,(addr-1)%4, description)
|
||||||
#define UNJOIN OPCODE_UNJOIN,0,0,
|
#define UNJOIN OPCODE_UNJOIN,0,0,
|
||||||
|
@ -376,6 +425,9 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = {
|
||||||
#define VIRTUAL_TURNOUT(id,description...) OPCODE_PINTURNOUT,V(id),OPCODE_PAD,V(0),
|
#define VIRTUAL_TURNOUT(id,description...) OPCODE_PINTURNOUT,V(id),OPCODE_PAD,V(0),
|
||||||
#define WITHROTTLE(msg) PRINT(msg)
|
#define WITHROTTLE(msg) PRINT(msg)
|
||||||
#define WAITFOR(pin) OPCODE_WAITFOR,V(pin),
|
#define WAITFOR(pin) OPCODE_WAITFOR,V(pin),
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
#define WAITFORTT(turntable_id) OPCODE_WAITFORTT,V(turntable_id),
|
||||||
|
#endif
|
||||||
#define XFOFF(cab,func) OPCODE_XFOFF,V(cab),OPCODE_PAD,V(func),
|
#define XFOFF(cab,func) OPCODE_XFOFF,V(cab),OPCODE_PAD,V(func),
|
||||||
#define XFON(cab,func) OPCODE_XFON,V(cab),OPCODE_PAD,V(func),
|
#define XFON(cab,func) OPCODE_XFON,V(cab),OPCODE_PAD,V(func),
|
||||||
|
|
||||||
|
|
|
@ -176,6 +176,13 @@ bool IODevice::exists(VPIN vpin) {
|
||||||
return findDevice(vpin) != NULL;
|
return findDevice(vpin) != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return the status of the device att vpin.
|
||||||
|
uint8_t IODevice::getStatus(VPIN vpin) {
|
||||||
|
IODevice *dev = findDevice(vpin);
|
||||||
|
if (!dev) return false;
|
||||||
|
return dev->_deviceState;
|
||||||
|
}
|
||||||
|
|
||||||
// check whether the pin supports notification. If so, then regular _read calls are not required.
|
// check whether the pin supports notification. If so, then regular _read calls are not required.
|
||||||
bool IODevice::hasCallback(VPIN vpin) {
|
bool IODevice::hasCallback(VPIN vpin) {
|
||||||
IODevice *dev = findDevice(vpin);
|
IODevice *dev = findDevice(vpin);
|
||||||
|
|
18
IODevice.h
18
IODevice.h
|
@ -27,17 +27,6 @@
|
||||||
// Define symbol DIAG_LOOPTIMES to enable CS loop execution time to be reported
|
// Define symbol DIAG_LOOPTIMES to enable CS loop execution time to be reported
|
||||||
//#define DIAG_LOOPTIMES
|
//#define DIAG_LOOPTIMES
|
||||||
|
|
||||||
// Define symbol IO_NO_HAL to reduce FLASH footprint when HAL features not required
|
|
||||||
// The HAL is disabled by default on Nano and Uno platforms, because of limited flash space.
|
|
||||||
#include "defines.h"
|
|
||||||
#if defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_UNO)
|
|
||||||
#if defined(DISABLE_DIAG) && defined(DISABLE_EEPROM) && defined(DISABLE_PROG)
|
|
||||||
#warning you have sacrificed DIAG for HAL
|
|
||||||
#else
|
|
||||||
#define IO_NO_HAL
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Define symbol IO_SWITCH_OFF_SERVO to set the PCA9685 output to 0 when an
|
// Define symbol IO_SWITCH_OFF_SERVO to set the PCA9685 output to 0 when an
|
||||||
// animation has completed. This switches off the servo motor, preventing
|
// animation has completed. This switches off the servo motor, preventing
|
||||||
// the continuous buzz sometimes found on servos, and reducing the
|
// the continuous buzz sometimes found on servos, and reducing the
|
||||||
|
@ -165,6 +154,9 @@ public:
|
||||||
// exists checks whether there is a device owning the specified vpin
|
// exists checks whether there is a device owning the specified vpin
|
||||||
static bool exists(VPIN vpin);
|
static bool exists(VPIN vpin);
|
||||||
|
|
||||||
|
// getStatus returns the state of the device at the specified vpin
|
||||||
|
static uint8_t getStatus(VPIN vpin);
|
||||||
|
|
||||||
// Enable shared interrupt on specified pin for GPIO extender modules. The extender module
|
// Enable shared interrupt on specified pin for GPIO extender modules. The extender module
|
||||||
// should pull down this pin when requesting a scan. The pin may be shared by multiple modules.
|
// should pull down this pin when requesting a scan. The pin may be shared by multiple modules.
|
||||||
// Without the shared interrupt, input states are scanned periodically to detect changes on
|
// Without the shared interrupt, input states are scanned periodically to detect changes on
|
||||||
|
@ -388,6 +380,7 @@ private:
|
||||||
uint8_t *_pinInUse;
|
uint8_t *_pinInUse;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
/*
|
/*
|
||||||
* IODevice subclass for EX-Turntable.
|
* IODevice subclass for EX-Turntable.
|
||||||
|
@ -416,10 +409,13 @@ private:
|
||||||
void _begin() override;
|
void _begin() override;
|
||||||
void _loop(unsigned long currentMicros) override;
|
void _loop(unsigned long currentMicros) override;
|
||||||
int _read(VPIN vpin) override;
|
int _read(VPIN vpin) override;
|
||||||
|
void _broadcastStatus (VPIN vpin, uint8_t status);
|
||||||
void _writeAnalogue(VPIN vpin, int value, uint8_t activity, uint16_t duration) override;
|
void _writeAnalogue(VPIN vpin, int value, uint8_t activity, uint16_t duration) override;
|
||||||
void _display() override;
|
void _display() override;
|
||||||
uint8_t _stepperStatus;
|
uint8_t _stepperStatus;
|
||||||
|
uint8_t _previousStatus;
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
|
@ -20,20 +20,21 @@
|
||||||
/*
|
/*
|
||||||
* The IO_EXTurntable device driver is used to control a turntable via an Arduino with a stepper motor over I2C.
|
* 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.
|
* The EX-Turntable code lives in a separate repo (https://github.com/DCC-EX/EX-Turntable) 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:
|
* This device driver sends a step position to EX-Turntable to indicate the step position to move to using either of these commands:
|
||||||
* <D TT vpin steps activity> in the serial console
|
* <D TT vpin steps activity> in the serial console
|
||||||
* MOVETT(vpin, steps, activity) in EX-RAIL
|
* MOVETT(vpin, steps, activity) in EX-RAIL
|
||||||
* Refer to the documentation for further information including the valid activities.
|
* Refer to the documentation for further information including the valid activities.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef IO_EXTurntable_h
|
|
||||||
#define IO_EXTurntable_h
|
|
||||||
|
|
||||||
#include "IODevice.h"
|
#include "IODevice.h"
|
||||||
#include "I2CManager.h"
|
#include "I2CManager.h"
|
||||||
#include "DIAG.h"
|
#include "DIAG.h"
|
||||||
|
#include "Turntables.h"
|
||||||
|
#include "CommandDistributor.h"
|
||||||
|
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
|
||||||
void EXTurntable::create(VPIN firstVpin, int nPins, I2CAddress I2CAddress) {
|
void EXTurntable::create(VPIN firstVpin, int nPins, I2CAddress I2CAddress) {
|
||||||
new EXTurntable(firstVpin, nPins, I2CAddress);
|
new EXTurntable(firstVpin, nPins, I2CAddress);
|
||||||
|
@ -44,6 +45,8 @@ EXTurntable::EXTurntable(VPIN firstVpin, int nPins, I2CAddress I2CAddress) {
|
||||||
_firstVpin = firstVpin;
|
_firstVpin = firstVpin;
|
||||||
_nPins = nPins;
|
_nPins = nPins;
|
||||||
_I2CAddress = I2CAddress;
|
_I2CAddress = I2CAddress;
|
||||||
|
_stepperStatus = 0;
|
||||||
|
_previousStatus = 0;
|
||||||
addDevice(this);
|
addDevice(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +54,7 @@ EXTurntable::EXTurntable(VPIN firstVpin, int nPins, I2CAddress I2CAddress) {
|
||||||
void EXTurntable::_begin() {
|
void EXTurntable::_begin() {
|
||||||
I2CManager.begin();
|
I2CManager.begin();
|
||||||
if (I2CManager.exists(_I2CAddress)) {
|
if (I2CManager.exists(_I2CAddress)) {
|
||||||
|
DIAG(F("EX-Turntable device found, I2C:%s"), _I2CAddress.toString());
|
||||||
#ifdef DIAG_IO
|
#ifdef DIAG_IO
|
||||||
_display();
|
_display();
|
||||||
#endif
|
#endif
|
||||||
|
@ -67,15 +71,17 @@ void EXTurntable::_loop(unsigned long currentMicros) {
|
||||||
uint8_t readBuffer[1];
|
uint8_t readBuffer[1];
|
||||||
I2CManager.read(_I2CAddress, readBuffer, 1);
|
I2CManager.read(_I2CAddress, readBuffer, 1);
|
||||||
_stepperStatus = readBuffer[0];
|
_stepperStatus = readBuffer[0];
|
||||||
// DIAG(F("Turntable-EX returned status: %d"), _stepperStatus);
|
if (_stepperStatus != _previousStatus && _stepperStatus == 0) { // Broadcast when a rotation finishes
|
||||||
delayUntil(currentMicros + 500000); // Wait 500ms before checking again, turntables turn slowly
|
_broadcastStatus(_firstVpin, _stepperStatus);
|
||||||
|
_previousStatus = _stepperStatus;
|
||||||
|
}
|
||||||
|
delayUntil(currentMicros + 100000); // Wait 100ms before checking again
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read returns status as obtained in our loop.
|
// Read returns status as obtained in our loop.
|
||||||
// Return false if our status value is invalid.
|
// Return false if our status value is invalid.
|
||||||
int EXTurntable::_read(VPIN vpin) {
|
int EXTurntable::_read(VPIN vpin) {
|
||||||
if (_deviceState == DEVSTATE_FAILED) return 0;
|
if (_deviceState == DEVSTATE_FAILED) return 0;
|
||||||
// DIAG(F("_read status: %d"), _stepperStatus);
|
|
||||||
if (_stepperStatus > 1) {
|
if (_stepperStatus > 1) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -83,6 +89,15 @@ int EXTurntable::_read(VPIN vpin) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If a status change has occurred for a turntable object, broadcast it
|
||||||
|
void EXTurntable::_broadcastStatus (VPIN vpin, uint8_t status) {
|
||||||
|
Turntable *tto = Turntable::getByVpin(vpin);
|
||||||
|
if (tto) {
|
||||||
|
tto->setMoving(status);
|
||||||
|
CommandDistributor::broadcastTurntable(tto->getId(), tto->getPosition(), status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// writeAnalogue to send the steps and activity to Turntable-EX.
|
// writeAnalogue to send the steps and activity to Turntable-EX.
|
||||||
// Sends 3 bytes containing the MSB and LSB of the step count, and activity.
|
// Sends 3 bytes containing the MSB and LSB of the step count, and activity.
|
||||||
// value contains the steps, bit shifted to MSB + LSB.
|
// value contains the steps, bit shifted to MSB + LSB.
|
||||||
|
@ -100,6 +115,7 @@ int EXTurntable::_read(VPIN vpin) {
|
||||||
// Acc_Off = 9 // Turn accessory pin off
|
// Acc_Off = 9 // Turn accessory pin off
|
||||||
void EXTurntable::_writeAnalogue(VPIN vpin, int value, uint8_t activity, uint16_t duration) {
|
void EXTurntable::_writeAnalogue(VPIN vpin, int value, uint8_t activity, uint16_t duration) {
|
||||||
if (_deviceState == DEVSTATE_FAILED) return;
|
if (_deviceState == DEVSTATE_FAILED) return;
|
||||||
|
if (value < 0) return;
|
||||||
uint8_t stepsMSB = value >> 8;
|
uint8_t stepsMSB = value >> 8;
|
||||||
uint8_t stepsLSB = value & 0xFF;
|
uint8_t stepsLSB = value & 0xFF;
|
||||||
#ifdef DIAG_IO
|
#ifdef DIAG_IO
|
||||||
|
@ -109,6 +125,8 @@ void EXTurntable::_writeAnalogue(VPIN vpin, int value, uint8_t activity, uint16_
|
||||||
_I2CAddress.toString(), stepsMSB, stepsLSB, activity);
|
_I2CAddress.toString(), stepsMSB, stepsLSB, activity);
|
||||||
#endif
|
#endif
|
||||||
_stepperStatus = 1; // Tell the device driver Turntable-EX is busy
|
_stepperStatus = 1; // Tell the device driver Turntable-EX is busy
|
||||||
|
_previousStatus = _stepperStatus;
|
||||||
|
_broadcastStatus(vpin, _stepperStatus); // Broadcast when the rotation starts
|
||||||
I2CManager.write(_I2CAddress, 3, stepsMSB, stepsLSB, activity);
|
I2CManager.write(_I2CAddress, 3, stepsMSB, stepsLSB, activity);
|
||||||
}
|
}
|
||||||
|
|
268
Turntables.cpp
Normal file
268
Turntables.cpp
Normal file
|
@ -0,0 +1,268 @@
|
||||||
|
/*
|
||||||
|
* © 2023 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "defines.h"
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "Turntables.h"
|
||||||
|
#include "StringFormatter.h"
|
||||||
|
#include "CommandDistributor.h"
|
||||||
|
#include "EXRAIL2.h"
|
||||||
|
#include "DCC.h"
|
||||||
|
|
||||||
|
// No turntable support without HAL
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Protected static data
|
||||||
|
*/
|
||||||
|
Turntable *Turntable::_firstTurntable = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Public static data
|
||||||
|
*/
|
||||||
|
int Turntable::turntablelistHash = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Protected static functions
|
||||||
|
*/
|
||||||
|
// Add new turntable to end of list
|
||||||
|
|
||||||
|
void Turntable::add(Turntable *tto) {
|
||||||
|
if (!_firstTurntable) {
|
||||||
|
_firstTurntable = tto;
|
||||||
|
} else {
|
||||||
|
Turntable *ptr = _firstTurntable;
|
||||||
|
for ( ; ptr->_nextTurntable!=0; ptr=ptr->_nextTurntable) {}
|
||||||
|
ptr->_nextTurntable = tto;
|
||||||
|
}
|
||||||
|
turntablelistHash++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a position
|
||||||
|
void Turntable::addPosition(uint8_t idx, uint16_t value, uint16_t angle) {
|
||||||
|
_turntablePositions.insert(idx, value, angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get value for position
|
||||||
|
uint16_t Turntable::getPositionValue(uint8_t position) {
|
||||||
|
TurntablePosition* currentPosition = _turntablePositions.getHead();
|
||||||
|
while (currentPosition) {
|
||||||
|
if (currentPosition->index == position) {
|
||||||
|
return currentPosition->data;
|
||||||
|
}
|
||||||
|
currentPosition = currentPosition->next;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get value for position
|
||||||
|
uint16_t Turntable::getPositionAngle(uint8_t position) {
|
||||||
|
TurntablePosition* currentPosition = _turntablePositions.getHead();
|
||||||
|
while (currentPosition) {
|
||||||
|
if (currentPosition->index == position) {
|
||||||
|
return currentPosition->angle;
|
||||||
|
}
|
||||||
|
currentPosition = currentPosition->next;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the count of positions associated with the turntable
|
||||||
|
uint8_t Turntable::getPositionCount() {
|
||||||
|
TurntablePosition* currentPosition = _turntablePositions.getHead();
|
||||||
|
uint8_t count = 0;
|
||||||
|
while (currentPosition) {
|
||||||
|
count++;
|
||||||
|
currentPosition = currentPosition->next;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Public static functions
|
||||||
|
*/
|
||||||
|
// Find turntable from list
|
||||||
|
Turntable *Turntable::get(uint16_t id) {
|
||||||
|
for (Turntable *tto = _firstTurntable; tto != nullptr; tto = tto->_nextTurntable)
|
||||||
|
if (tto->_turntableData.id == id) return tto;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find turntable via Vpin
|
||||||
|
Turntable *Turntable::getByVpin(VPIN vpin) {
|
||||||
|
for (Turntable *tto = _firstTurntable; tto != nullptr; tto = tto->_nextTurntable) {
|
||||||
|
if (tto->isEXTT()) {
|
||||||
|
EXTTTurntable *exttTto = static_cast<EXTTTurntable*>(tto);
|
||||||
|
if (exttTto->getVpin() == vpin) {
|
||||||
|
return tto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current position for turntable with the specified ID
|
||||||
|
uint8_t Turntable::getPosition(uint16_t id) {
|
||||||
|
Turntable *tto = get(id);
|
||||||
|
if (!tto) return false;
|
||||||
|
return tto->getPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Got the moving state of the specified turntable
|
||||||
|
bool Turntable::ttMoving(uint16_t id) {
|
||||||
|
Turntable *tto = get(id);
|
||||||
|
if (!tto) return false;
|
||||||
|
return tto->isMoving();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initiate a turntable move
|
||||||
|
bool Turntable::setPosition(uint16_t id, uint8_t position, uint8_t activity) {
|
||||||
|
#if defined(DIAG_IO)
|
||||||
|
DIAG(F("Rotate turntable %d to position %d, activity %d)"), id, position, activity);
|
||||||
|
#endif
|
||||||
|
Turntable *tto = Turntable::get(id);
|
||||||
|
if (!tto) return false;
|
||||||
|
if (tto->isMoving()) return false;
|
||||||
|
bool ok = tto->setPositionInternal(position, activity);
|
||||||
|
|
||||||
|
if (ok) {
|
||||||
|
// We only deal with broadcasts for DCC turntables here, EXTT in the device driver
|
||||||
|
if (!tto->isEXTT()) {
|
||||||
|
CommandDistributor::broadcastTurntable(id, position, false);
|
||||||
|
}
|
||||||
|
// Trigger EXRAIL rotateEvent for both types here if changed
|
||||||
|
#if defined(EXRAIL_ACTIVE)
|
||||||
|
bool rotated = false;
|
||||||
|
if (position != tto->_previousPosition) rotated = true;
|
||||||
|
RMFT2::rotateEvent(id, rotated);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************************
|
||||||
|
* EXTTTurntable - EX-Turntable device.
|
||||||
|
*
|
||||||
|
*************************************************************************************/
|
||||||
|
// Private constructor
|
||||||
|
EXTTTurntable::EXTTTurntable(uint16_t id, VPIN vpin) :
|
||||||
|
Turntable(id, TURNTABLE_EXTT)
|
||||||
|
{
|
||||||
|
_exttTurntableData.vpin = vpin;
|
||||||
|
}
|
||||||
|
|
||||||
|
using DevState = IODevice::DeviceStateEnum;
|
||||||
|
|
||||||
|
// Create function
|
||||||
|
Turntable *EXTTTurntable::create(uint16_t id, VPIN vpin) {
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
Turntable *tto = get(id);
|
||||||
|
if (tto) {
|
||||||
|
if (tto->isType(TURNTABLE_EXTT)) {
|
||||||
|
EXTTTurntable *extt = (EXTTTurntable *)tto;
|
||||||
|
extt->_exttTurntableData.vpin = vpin;
|
||||||
|
return tto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!IODevice::exists(vpin)) return nullptr;
|
||||||
|
if (IODevice::getStatus(vpin) == DevState::DEVSTATE_FAILED) return nullptr;
|
||||||
|
if (Turntable::getByVpin(vpin)) return nullptr;
|
||||||
|
tto = (Turntable *)new EXTTTurntable(id, vpin);
|
||||||
|
DIAG(F("Turntable 0x%x size %d size %d"), tto, sizeof(Turntable), sizeof(struct TurntableData));
|
||||||
|
return tto;
|
||||||
|
#else
|
||||||
|
(void)id;
|
||||||
|
(void)vpin;
|
||||||
|
return NULL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void EXTTTurntable::print(Print *stream) {
|
||||||
|
StringFormatter::send(stream, F("<i %d EXTURNTABLE %d>\n"), _turntableData.id, _exttTurntableData.vpin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// EX-Turntable specific code for moving to the specified position
|
||||||
|
bool EXTTTurntable::setPositionInternal(uint8_t position, uint8_t activity) {
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
int16_t value;
|
||||||
|
if (position == 0) {
|
||||||
|
value = 0; // Position 0 is just to send activities
|
||||||
|
} else {
|
||||||
|
if (activity > 1) return false; // If sending a position update, only phase changes valid (0|1)
|
||||||
|
value = getPositionValue(position); // Get position value from position list
|
||||||
|
}
|
||||||
|
if (position > 0 && !value) return false; // Return false if it's not a valid position
|
||||||
|
// Set position via device driver
|
||||||
|
_previousPosition = _turntableData.position;
|
||||||
|
_turntableData.position = position;
|
||||||
|
EXTurntable::writeAnalogue(_exttTurntableData.vpin, value, activity);
|
||||||
|
#else
|
||||||
|
(void)position;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************************
|
||||||
|
* DCCTurntable - DCC Turntable device.
|
||||||
|
*
|
||||||
|
*************************************************************************************/
|
||||||
|
// Private constructor
|
||||||
|
DCCTurntable::DCCTurntable(uint16_t id) : Turntable(id, TURNTABLE_DCC) {}
|
||||||
|
|
||||||
|
// Create function
|
||||||
|
Turntable *DCCTurntable::create(uint16_t id) {
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
Turntable *tto = get(id);
|
||||||
|
if (!tto) {
|
||||||
|
tto = (Turntable *)new DCCTurntable(id);
|
||||||
|
DIAG(F("Turntable 0x%x size %d size %d"), tto, sizeof(Turntable), sizeof(struct TurntableData));
|
||||||
|
}
|
||||||
|
return tto;
|
||||||
|
#else
|
||||||
|
(void)id;
|
||||||
|
return NULL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void DCCTurntable::print(Print *stream) {
|
||||||
|
StringFormatter::send(stream, F("<i %d DCCTURNTABLE>\n"), _turntableData.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// EX-Turntable specific code for moving to the specified position
|
||||||
|
bool DCCTurntable::setPositionInternal(uint8_t position, uint8_t activity) {
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
int16_t value = getPositionValue(position);
|
||||||
|
if (position == 0 || !value) return false; // Return false if it's not a valid position
|
||||||
|
// Set position via device driver
|
||||||
|
int16_t addr=value>>3;
|
||||||
|
int16_t subaddr=(value>>1) & 0x03;
|
||||||
|
bool active=value & 0x01;
|
||||||
|
_previousPosition = _turntableData.position;
|
||||||
|
_turntableData.position = position;
|
||||||
|
DCC::setAccessory(addr, subaddr, active);
|
||||||
|
#else
|
||||||
|
(void)position;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
243
Turntables.h
Normal file
243
Turntables.h
Normal file
|
@ -0,0 +1,243 @@
|
||||||
|
/*
|
||||||
|
* © 2023 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TURNTABLES_H
|
||||||
|
#define TURNTABLES_H
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "IODevice.h"
|
||||||
|
#include "StringFormatter.h"
|
||||||
|
|
||||||
|
// No turntable support without HAL
|
||||||
|
#ifndef IO_NO_HAL
|
||||||
|
|
||||||
|
// Turntable type definitions
|
||||||
|
// EXTT = EX-Turntable
|
||||||
|
// DCC = DCC accessory turntables - to be added later
|
||||||
|
enum {
|
||||||
|
TURNTABLE_EXTT = 0,
|
||||||
|
TURNTABLE_DCC = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*************************************************************************************
|
||||||
|
* Turntable positions.
|
||||||
|
*
|
||||||
|
*************************************************************************************/
|
||||||
|
struct TurntablePosition {
|
||||||
|
uint8_t index;
|
||||||
|
uint16_t data;
|
||||||
|
uint16_t angle;
|
||||||
|
TurntablePosition* next;
|
||||||
|
|
||||||
|
TurntablePosition(uint8_t idx, uint16_t value, uint16_t angle) : index(idx), data(value), angle(angle), next(nullptr) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class TurntablePositionList {
|
||||||
|
public:
|
||||||
|
TurntablePositionList() : head(nullptr) {}
|
||||||
|
|
||||||
|
void insert(uint8_t idx, uint16_t value, uint16_t angle) {
|
||||||
|
TurntablePosition* newPosition = new TurntablePosition(idx, value, angle);
|
||||||
|
if(!head) {
|
||||||
|
head = newPosition;
|
||||||
|
} else {
|
||||||
|
newPosition->next = head;
|
||||||
|
head = newPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TurntablePosition* getHead() {
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
TurntablePosition* head;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************************
|
||||||
|
* Turntable - Base class for turntables.
|
||||||
|
*
|
||||||
|
*************************************************************************************/
|
||||||
|
|
||||||
|
class Turntable {
|
||||||
|
protected:
|
||||||
|
/*
|
||||||
|
* Object data
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Data common to all turntable types
|
||||||
|
struct TurntableData {
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
bool hidden : 1;
|
||||||
|
bool turntableType : 1;
|
||||||
|
uint8_t position : 6; // Allows up to 63 positions including 0/home
|
||||||
|
};
|
||||||
|
uint8_t flags;
|
||||||
|
};
|
||||||
|
uint16_t id;
|
||||||
|
} _turntableData;
|
||||||
|
|
||||||
|
// Pointer to next turntable object
|
||||||
|
Turntable *_nextTurntable = 0;
|
||||||
|
|
||||||
|
// Linked list for positions
|
||||||
|
TurntablePositionList _turntablePositions;
|
||||||
|
|
||||||
|
// Store the previous position to allow checking for changes
|
||||||
|
uint8_t _previousPosition = 0;
|
||||||
|
|
||||||
|
// Store the current state of the turntable
|
||||||
|
bool _isMoving = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
Turntable(uint16_t id, uint8_t turntableType) {
|
||||||
|
_turntableData.id = id;
|
||||||
|
_turntableData.turntableType = turntableType;
|
||||||
|
_turntableData.hidden = false;
|
||||||
|
_turntableData.position = 0;
|
||||||
|
add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Static data
|
||||||
|
*/
|
||||||
|
static Turntable *_firstTurntable;
|
||||||
|
static int _turntablelistHash;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Virtual functions
|
||||||
|
*/
|
||||||
|
virtual bool setPositionInternal(uint8_t position, uint8_t activity) = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Static functions
|
||||||
|
*/
|
||||||
|
static void add(Turntable *tto);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static Turntable *get(uint16_t id);
|
||||||
|
static Turntable *getByVpin(VPIN vpin);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Static data
|
||||||
|
*/
|
||||||
|
static int turntablelistHash;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Public base class functions
|
||||||
|
*/
|
||||||
|
inline uint8_t getPosition() { return _turntableData.position; }
|
||||||
|
inline bool isHidden() { return _turntableData.hidden; }
|
||||||
|
inline void setHidden(bool h) {_turntableData.hidden=h; }
|
||||||
|
inline bool isType(uint8_t type) { return _turntableData.turntableType == type; }
|
||||||
|
inline bool isEXTT() const { return _turntableData.turntableType == TURNTABLE_EXTT; }
|
||||||
|
inline uint16_t getId() { return _turntableData.id; }
|
||||||
|
inline Turntable *next() { return _nextTurntable; }
|
||||||
|
void printState(Print *stream);
|
||||||
|
void addPosition(uint8_t idx, uint16_t value, uint16_t angle);
|
||||||
|
uint16_t getPositionValue(uint8_t position);
|
||||||
|
uint16_t getPositionAngle(uint8_t position);
|
||||||
|
uint8_t getPositionCount();
|
||||||
|
bool isMoving() { return _isMoving; }
|
||||||
|
void setMoving(bool moving) { _isMoving=moving; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Virtual functions
|
||||||
|
*/
|
||||||
|
virtual void print(Print *stream) {
|
||||||
|
(void)stream; // suppress compiler warnings
|
||||||
|
}
|
||||||
|
virtual ~Turntable() {} // Destructor
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Public static functions
|
||||||
|
*/
|
||||||
|
inline static bool exists(uint16_t id) { return get(id) != 0; }
|
||||||
|
static bool setPosition(uint16_t id, uint8_t position, uint8_t activity=0);
|
||||||
|
static uint8_t getPosition(uint16_t id);
|
||||||
|
static bool ttMoving(uint16_t id);
|
||||||
|
inline static Turntable *first() { return _firstTurntable; }
|
||||||
|
static bool printAll(Print *stream) {
|
||||||
|
bool gotOne = false;
|
||||||
|
for (Turntable *tto = _firstTurntable; tto != 0; tto = tto->_nextTurntable)
|
||||||
|
if (!tto->isHidden()) {
|
||||||
|
gotOne = true;
|
||||||
|
StringFormatter::send(stream, F("<i %d %d>\n"), tto->getId(), tto->getPosition());
|
||||||
|
}
|
||||||
|
return gotOne;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/*************************************************************************************
|
||||||
|
* EXTTTurntable - EX-Turntable device.
|
||||||
|
*
|
||||||
|
*************************************************************************************/
|
||||||
|
class EXTTTurntable : public Turntable {
|
||||||
|
private:
|
||||||
|
// EXTTTurntableData contains device specific data
|
||||||
|
struct EXTTTurntableData {
|
||||||
|
VPIN vpin;
|
||||||
|
} _exttTurntableData;
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
EXTTTurntable(uint16_t id, VPIN vpin);
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Create function
|
||||||
|
static Turntable *create(uint16_t id, VPIN vpin);
|
||||||
|
void print(Print *stream) override;
|
||||||
|
VPIN getVpin() const { return _exttTurntableData.vpin; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// EX-Turntable specific code for setting position
|
||||||
|
bool setPositionInternal(uint8_t position, uint8_t activity) override;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/*************************************************************************************
|
||||||
|
* DCCTurntable - DCC accessory Turntable device.
|
||||||
|
*
|
||||||
|
*************************************************************************************/
|
||||||
|
class DCCTurntable : public Turntable {
|
||||||
|
private:
|
||||||
|
// Constructor
|
||||||
|
DCCTurntable(uint16_t id);
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Create function
|
||||||
|
static Turntable *create(uint16_t id);
|
||||||
|
void print(Print *stream) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// DCC specific code for setting position
|
||||||
|
bool setPositionInternal(uint8_t position, uint8_t activity=0) override;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
13
defines.h
13
defines.h
|
@ -213,6 +213,19 @@
|
||||||
//
|
//
|
||||||
#define WIFI_SERIAL_LINK_SPEED 115200
|
#define WIFI_SERIAL_LINK_SPEED 115200
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Define symbol IO_NO_HAL to reduce FLASH footprint when HAL features not required
|
||||||
|
// The HAL is disabled by default on Nano and Uno platforms, because of limited flash space.
|
||||||
|
//
|
||||||
|
#if defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_UNO)
|
||||||
|
#if defined(DISABLE_DIAG) && defined(DISABLE_EEPROM) && defined(DISABLE_PROG)
|
||||||
|
#warning you have sacrificed DIAG for HAL
|
||||||
|
#else
|
||||||
|
#define IO_NO_HAL
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#if __has_include ( "myAutomation.h")
|
#if __has_include ( "myAutomation.h")
|
||||||
#if defined(HAS_ENOUGH_MEMORY) || defined(DISABLE_EEPROM) || defined(DISABLE_PROG)
|
#if defined(HAS_ENOUGH_MEMORY) || defined(DISABLE_EEPROM) || defined(DISABLE_PROG)
|
||||||
#define EXRAIL_ACTIVE
|
#define EXRAIL_ACTIVE
|
||||||
|
|
|
@ -30,7 +30,7 @@ include_dir = .
|
||||||
|
|
||||||
[env]
|
[env]
|
||||||
build_flags = -Wall -Wextra
|
build_flags = -Wall -Wextra
|
||||||
monitor_filters = time
|
; monitor_filters = time
|
||||||
; lib_deps = adafruit/Adafruit ST7735 and ST7789 Library @ ^1.10.0
|
; lib_deps = adafruit/Adafruit ST7735 and ST7789 Library @ ^1.10.0
|
||||||
|
|
||||||
[env:samd21-dev-usb]
|
[env:samd21-dev-usb]
|
||||||
|
|
|
@ -3,7 +3,10 @@
|
||||||
|
|
||||||
#include "StringFormatter.h"
|
#include "StringFormatter.h"
|
||||||
|
|
||||||
#define VERSION "5.1.4"
|
#define VERSION "5.1.5"
|
||||||
|
// 5.1.5 - Added turntable object and EXRAIL commands
|
||||||
|
// - <I ...>, <JO ...>, <JP ...> - turntable commands
|
||||||
|
// - DCC_TURNTABLE, EXTT_TURNTABLE, IFTTPOSITION, ONROTATE, ROTATE, ROTATE_DCC, TT_ADDPOSITION, WAITFORTT EXRAIL
|
||||||
// 5.1.4 - Added ONOVERLOAD & AFTEROVERLOAD to EXRAIL
|
// 5.1.4 - Added ONOVERLOAD & AFTEROVERLOAD to EXRAIL
|
||||||
// 5.1.3 - Make parser more fool proof
|
// 5.1.3 - Make parser more fool proof
|
||||||
// 5.1.2 - Bugfix: ESP32 30ms off time
|
// 5.1.2 - Bugfix: ESP32 30ms off time
|
||||||
|
|
Loading…
Reference in New Issue
Block a user