diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h
index df4e413..28f74c1 100644
--- a/EXRAIL2MacroReset.h
+++ b/EXRAIL2MacroReset.h
@@ -875,7 +875,7 @@
#define ONTHROW(turnout_id)
/**
* @def ONCHANGE(vpin)
- * @brief Toratry encoder change starts task here (This is obscurely different from ONSENSOR which will be merged in a later release.)
+ * @brief Rotary encoder change starts task here (This is obscurely different from ONSENSOR which will be merged in a later release.)
* @param vpin
*/
#define ONCHANGE(vpin)
diff --git a/EXRAILAsserts.h b/EXRAILAsserts.h
new file mode 100644
index 0000000..dc83c18
--- /dev/null
+++ b/EXRAILAsserts.h
@@ -0,0 +1,164 @@
+/*
+ * © 2020-2025 Chris Harlow
+ * 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 .
+ */
+
+// This file checks the myAutomation for errors by generating a list of compile time asserts.
+
+// Assert Pass 1 Collect sequence numbers.
+#include "EXRAIL2MacroReset.h"
+#undef AUTOMATION
+#define AUTOMATION(id, description) id,
+#undef ROUTE
+#define ROUTE(id, description) id,
+#undef SEQUENCE
+#define SEQUENCE(id) id,
+constexpr int16_t compileTimeSequenceList[]={
+ #include "myAutomation.h"
+ 0
+ };
+constexpr int16_t stuffSize=sizeof(compileTimeSequenceList)/sizeof(int16_t) - 1;
+
+
+// Compile time function to check for sequence number duplication
+constexpr int16_t seqCount(const int16_t value, const int16_t pos=0, const int16_t count=0 ) {
+ return pos>=stuffSize? count :
+ seqCount(value,pos+1,count+((compileTimeSequenceList[pos]==value)?1:0));
+}
+
+
+// Build a compile time blacklist of pin numbers.
+// Includes those defined in defaults.h for the cpu (PIN_BLACKLIST)
+// and cheats in the motor shield pins from config.h (MOTOR_SHIELD_TYPE)
+// for reference the MotorDriver constructor is:
+// MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8_t brake_pin, byte current_pin,
+// float senseFactor, unsigned int tripMilliamps, byte faultPin);
+
+// create capture macros to reinterpret MOTOR_SHIELD_TYPE from configuration
+#define new
+#define MotorDriver(power_pin,signal_pin,signal_pin2, \
+ brake_pin,current_pin,senseFactor,tripMilliamps,faultPin) \
+ power_pin,signal_pin,signal_pin2,brake_pin,current_pin,faultPin
+#ifndef PIN_BLACKLIST
+ #define PIN_BLACKLIST UNUSED_PIN
+#endif
+#define MDFURKLE(stuff) MDFURKLE2(stuff)
+#define MDFURKLE2(description,...) REMOVE_TRAILING_COMMA(__VA_ARGS__)
+#define REMOVE_TRAILING_COMMA(...) __VA_ARGS__
+
+
+constexpr int16_t compileTimePinBlackList[]={
+ PIN_BLACKLIST, MDFURKLE(MOTOR_SHIELD_TYPE)
+ };
+constexpr int16_t pbSize=sizeof(compileTimePinBlackList)/sizeof(int16_t) - 1;
+
+
+// remove capture macros
+#undef new
+#undef MotorDriver
+
+// Compile time function to check for dangerous pins.
+constexpr bool unsafePin(const int16_t value, const uint16_t pos=0 ) {
+ return pos>=pbSize? false :
+ compileTimePinBlackList[pos]==value
+ || unsafePin(value,pos+1);
+}
+
+
+//pass 2 apply static asserts:
+// check call and follows etc for existing sequence numbers
+// check sequence numbers for duplicates
+// check range on LATCH/UNLATCH
+// check range on RESERVE/FREE
+// check range on SPEED/FWD/REV
+// check range on SET/RESET (pins that are not safe to use in EXRAIL)
+//
+// This pass generates no runtime data or code
+#include "EXRAIL2MacroReset.h"
+#undef ASPECT
+#define ASPECT(address,value) static_assert(address <=2044, "invalid Address"); \
+ static_assert(address>=-3, "Invalid value");
+
+// check references to sequences/routes/automations
+#undef CALL
+#define CALL(id) static_assert(seqCount(id)>0,"Sequence not found");
+#undef FOLLOW
+#define FOLLOW(id) static_assert(seqCount(id)>0,"Sequence not found");
+#undef START
+#define START(id) static_assert(seqCount(id)>0,"Sequence not found");
+#undef SENDLOCO
+#define SENDLOCO(cab,id) static_assert(seqCount(id)>0,"Sequence not found");
+#undef ROUTE_ACTIVE
+#define ROUTE_ACTIVE(id) static_assert(seqCount(id)>0,"Route not found");
+#undef ROUTE_INACTIVE
+#define ROUTE_INACTIVE(id) static_assert(seqCount(id)>0,"Route not found");
+#undef ROUTE_HIDDEN
+#define ROUTE_HIDDEN(id) static_assert(seqCount(id)>0,"Route not found");
+#undef ROUTE_DISABLED
+#define ROUTE_DISABLED(id) static_assert(seqCount(id)>0,"Route not found");
+#undef ROUTE_CAPTION
+#define ROUTE_CAPTION(id,caption) static_assert(seqCount(id)>0,"Route not found");
+
+
+#undef LATCH
+#define LATCH(id) static_assert(id>=0 && id=0 && id=0 && id=0 && id=0 && id=0 && speed<128,"Speed out of valid range 0-127");
+#undef FWD
+#define FWD(speed) static_assert(speed>=0 && speed<128,"Speed out of valid range 0-127");
+#undef REV
+#define REV(speed) static_assert(speed>=0 && speed<128,"Speed out of valid range 0-127");
+
+// check duplicate sequences
+#undef SEQUENCE
+#define SEQUENCE(id) static_assert(seqCount(id)==1,"Duplicate ROUTE/AUTOMATION/SEQUENCE " #id);
+#undef AUTOMATION
+#define AUTOMATION(id,description) static_assert(seqCount(id)==1,"Duplicate ROUTE/AUTOMATION/SEQUENCE " #id);
+#undef ROUTE
+#define ROUTE(id,description) static_assert(seqCount(id)==1,"Duplicate ROUTE/AUTOMATION/SEQUENCE " #id);
+
+// check dangerous pins
+#undef SET
+#define SET(vpin) static_assert(!unsafePin(vpin),"SET(" #vpin ") not safe to use in EXRAIL");
+#undef RESET
+#define RESET(vpin) static_assert(!unsafePin(vpin),"RESET(" #vpin ") not safe to use in EXRAIL");
+#undef BLINK
+#define BLINK(vpin,onDuty,offDuty) static_assert(!unsafePin(vpin),"BLINK(" #vpin ") not safe to use in EXRAIL");
+#undef SIGNAL
+#define SIGNAL(redpin,amberpin,greenpin) \
+ static_assert(!unsafePin(redpin),"RED " #redpin " not safe to use in EXRAIL"); \
+ static_assert(amberpin==0 ||!unsafePin(amberpin),"AMBER " #amberpin " not safe to use in EXRAIL"); \
+ static_assert(!unsafePin(greenpin),"GREEN " #greenpin " not safe to use in EXRAIL");
+#undef SIGNALH
+#define SIGNALH(redpin,amberpin,greenpin) \
+ static_assert(!unsafePin(redpin),"RED " #redpin " not safe to use in EXRAIL"); \
+ static_assert(amberpin==0 ||!unsafePin(amberpin),"AMBER " #amberpin " not safe to use in EXRAIL"); \
+ static_assert(!unsafePin(greenpin),"GREEN " #greenpin " not safe to use in EXRAIL");
+
+// and run the assert pass.
+#include "myAutomation.h"
diff --git a/EXRAILMacros.h b/EXRAILMacros.h
index 11ffdc1..d672c24 100644
--- a/EXRAILMacros.h
+++ b/EXRAILMacros.h
@@ -86,72 +86,8 @@
#define ALIAS(name,value...) const int name= #value[0] ? value+0: -__COUNTER__ ;
#include "myAutomation.h"
-// Pass 1d Detect sequence duplicates.
-// This pass generates no runtime data or code
-#include "EXRAIL2MacroReset.h"
-#undef AUTOMATION
-#define AUTOMATION(id, description) id,
-#undef ROUTE
-#define ROUTE(id, description) id,
-#undef SEQUENCE
-#define SEQUENCE(id) id,
-constexpr int16_t compileTimeSequenceList[]={
- #include "myAutomation.h"
- 0
- };
-constexpr int16_t stuffSize=sizeof(compileTimeSequenceList)/sizeof(int16_t) - 1;
-
-
-// Compile time function to check for sequence nos.
-constexpr bool hasseq(const int16_t value, const int16_t pos=0 ) {
- return pos>=stuffSize? false :
- compileTimeSequenceList[pos]==value
- || hasseq(value,pos+1);
-}
-
-// Compile time function to check for duplicate sequence nos.
-constexpr bool hasdup(const int16_t value, const int16_t pos ) {
- return pos>=stuffSize? false :
- compileTimeSequenceList[pos]==value
- || hasseq(value,pos+1)
- || hasdup(compileTimeSequenceList[pos],pos+1);
-}
-
-
-static_assert(!hasdup(compileTimeSequenceList[0],1),"Duplicate SEQUENCE/ROUTE/AUTOMATION detected");
-
-//pass 1s static asserts to
-// - check call and follows etc for existing sequence numbers
-// - check range on LATCH/UNLATCH
-// This pass generates no runtime data or code
-#include "EXRAIL2MacroReset.h"
-#undef ASPECT
-#define ASPECT(address,value) static_assert(address <=2044, "invalid Address"); \
- static_assert(address>=-3, "Invalid value");
-#undef CALL
-#define CALL(id) static_assert(hasseq(id),"Sequence not found");
-#undef FOLLOW
-#define FOLLOW(id) static_assert(hasseq(id),"Sequence not found");
-#undef START
-#define START(id) static_assert(hasseq(id),"Sequence not found");
-#undef SENDLOCO
-#define SENDLOCO(cab,id) static_assert(hasseq(id),"Sequence not found");
-#undef LATCH
-#define LATCH(id) static_assert(id>=0 && id=0 && id=0 && id=0 && id=0 && speed<128,"Speed out of valid range 0-127");
-#undef FWD
-#define FWD(speed) static_assert(speed>=0 && speed<128,"Speed out of valid range 0-127");
-#undef REV
-#define REV(speed) static_assert(speed>=0 && speed<128,"Speed out of valid range 0-127");
-
-#include "myAutomation.h"
+// Perform compile time asserts to check the script for errors
+#include "EXRAILAsserts.h"
// Pass 1g Implants STEALTH_GLOBAL in correct place
#include "EXRAIL2MacroReset.h"
@@ -607,9 +543,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
#define PAUSE OPCODE_PAUSE,0,0,
#define PICKUP_STASH(id) OPCODE_PICKUP_STASH,V(id),
#define PIN_TURNOUT(id,pin,description...) OPCODE_PINTURNOUT,V(id),OPCODE_PAD,V(pin),
-#ifndef DISABLE_PROG
#define POM(cv,value) OPCODE_POM,V(cv),OPCODE_PAD,V(value),
-#endif
#define POWEROFF OPCODE_POWEROFF,0,0,
#define POWERON OPCODE_POWERON,0,0,
#define PRINT(msg) OPCODE_PRINT,V(__COUNTER__ - StringMacroTracker2),
diff --git a/version.h b/version.h
index f32c980..d90b8ce 100644
--- a/version.h
+++ b/version.h
@@ -3,7 +3,8 @@
#include "StringFormatter.h"
-#define VERSION "5.5.16"
+#define VERSION "5.5.17"
+// 5.5.17 - Extensive new compile time checking in exrail scripts (duplicate sequences etc), no function change
// 5.5.16 - DOXYGEN comments in EXRAIL2MacroReset.h
// 5.5.15 - Support for F429ZI/F329ZI
// - Own mDNS support for (wired) Ethernet