From 3aabb51888d61d88c3c26a3617eccaa74e3f64d4 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Wed, 18 Sep 2024 17:06:00 +0100 Subject: [PATCH] Neopixel signals with blue-tint See Release Notes file --- EXRAIL2.cpp | 164 +++++++++++++++++++------------------- EXRAIL2.h | 34 +++++--- EXRAIL2Parser.cpp | 10 +-- EXRAILMacros.h | 23 +++--- FSH.h | 6 ++ Release_Notes/NeoPixel.md | 22 ++++- 6 files changed, 148 insertions(+), 111 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index f14a4de..84bdac4 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -73,6 +73,7 @@ RMFT2 * RMFT2::pausingTask=NULL; // Task causing a PAUSE. byte RMFT2::flags[MAX_FLAGS]; Print * RMFT2::LCCSerial=0; LookList * RMFT2::routeLookup=NULL; +LookList * RMFT2::signalLookup=NULL; LookList * RMFT2::onThrowLookup=NULL; LookList * RMFT2::onCloseLookup=NULL; LookList * RMFT2::onActivateLookup=NULL; @@ -207,16 +208,28 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) { // Second pass startup, define any turnouts or servos, set signals red // add sequences onRoutines to the lookups if (compileFeatures & FEATURE_SIGNAL) { + onRedLookup=LookListLoader(OPCODE_ONRED); onAmberLookup=LookListLoader(OPCODE_ONAMBER); onGreenLookup=LookListLoader(OPCODE_ONGREEN); - for (int sigslot=0;;sigslot++) { - int16_t sighandle=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigslot*8); - if (sighandle==0) break; // end of signal list - VPIN sigid = sighandle & SIGNAL_ID_MASK; - doSignal(sigid, SIGNAL_RED); - } - } + // Load the signal lookup with slot numbers in the signal table + int signalCount=0; + for (int16_t slot=0;;slot++) { + SIGNAL_DEFINITION signal=getSignalSlot(slot); + DIAG(F("Signal s=%d id=%d t=%d"),slot,signal.id,signal.type); + if (signal.type==sigtypeNoMoreSignals) break; + if (signal.type==sigtypeContinuation) continue; + signalCount++; + } + signalLookup=new LookList(signalCount); + for (int16_t slot=0;;slot++) { + SIGNAL_DEFINITION signal=getSignalSlot(slot); + if (signal.type==sigtypeNoMoreSignals) break; + if (signal.type==sigtypeContinuation) continue; + signalLookup->add(signal.id,slot); + doSignal(signal.id, SIGNAL_RED); + } + } int progCounter; for (progCounter=0;; SKIPOP){ @@ -1136,26 +1149,11 @@ void RMFT2::kill(const FSH * reason, int operand) { delete this; } -int16_t RMFT2::getSignalSlot(int16_t id) { - if (id > 0) { - int sigslot = 0; - int16_t sighandle = 0; - // Trundle down the signal list until we reach the end - while ((sighandle = GETHIGHFLASHW(RMFT2::SignalDefinitions, sigslot * 8)) != 0) - { - // sigid is the signal id used in RED/AMBER/GREEN macro - // for a LED signal it will be same as redpin - // but for a servo signal it will also have SERVO_SIGNAL_FLAG set. - VPIN sigid = sighandle & SIGNAL_ID_MASK; - if (sigid == (VPIN)id) // cast to keep compiler happy but id is positive - return sigslot; // found it - sigslot++; // keep looking - }; - } - // If we got here, we did not find the signal - DIAG(F("EXRAIL Signal %d not defined"), id); - return -1; +SIGNAL_DEFINITION RMFT2::getSignalSlot(int16_t slot) { + SIGNAL_DEFINITION signal; + COPYHIGHFLASH(&signal,SignalDefinitions,slot*sizeof(SIGNAL_DEFINITION),sizeof(SIGNAL_DEFINITION)); + return signal; } /* static */ void RMFT2::doSignal(int16_t id,char rag) { @@ -1168,90 +1166,97 @@ int16_t RMFT2::getSignalSlot(int16_t id) { else if (rag==SIGNAL_GREEN) onGreenLookup->handleEvent(F("GREEN"),id); else onAmberLookup->handleEvent(F("AMBER"),id); - int16_t sigslot=getSignalSlot(id); + auto sigslot=signalLookup->find(id); if (sigslot<0) return; // keep track of signal state setFlag(sigslot,rag,SIGNAL_MASK); // Correct signal definition found, get the rag values - int16_t sigpos=sigslot*8; - int16_t sighandle=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigpos); - VPIN redpin=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigpos+2); - VPIN amberpin=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigpos+4); - VPIN greenpin=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigpos+6); - //if (diag) DIAG(F("signal %d %d %d %d %d"),sigid,id,redpin,amberpin,greenpin); - - VPIN sigtype=sighandle & ~SIGNAL_ID_MASK; - VPIN sigid = sighandle & SIGNAL_ID_MASK; - - if (sigtype == SERVO_SIGNAL_FLAG) { - // A servo signal, the pin numbers are actually servo positions - // Note, setting a signal to a zero position has no effect. - int16_t servopos= rag==SIGNAL_RED? redpin: (rag==SIGNAL_GREEN? greenpin : amberpin); + auto signal=getSignalSlot(sigslot); + + switch (signal.type) { + case sigtypeSERVO: + { + auto servopos = rag==SIGNAL_RED? signal.redpin: (rag==SIGNAL_GREEN? signal.greenpin : signal.amberpin); //if (diag) DIAG(F("sigA %d %d"),id,servopos); if (servopos!=0) IODevice::writeAnalogue(id,servopos,PCA9685::Bounce); return; - } + } - - if (sigtype== DCC_SIGNAL_FLAG) { + case sigtypeDCC: + { // redpin,amberpin are the DCC addr,subaddr - DCC::setAccessory(redpin,amberpin, rag!=SIGNAL_RED); + DCC::setAccessory(signal.redpin,signal.amberpin, rag!=SIGNAL_RED); return; } - if (sigtype== DCCX_SIGNAL_FLAG) { + case sigtypeDCCX: + { // redpin,amberpin,greenpin are the 3 aspects - byte value=redpin; - if (rag==SIGNAL_AMBER) value=amberpin; - if (rag==SIGNAL_GREEN) value=greenpin; - DCC::setExtendedAccessory(sigid, value); + auto value=signal.redpin; + if (rag==SIGNAL_AMBER) value=signal.amberpin; + if (rag==SIGNAL_GREEN) value=signal.greenpin; + DCC::setExtendedAccessory(id, value); return; } -if (sigtype== NEOPIXEL_SIGNAL_FLAG) { +case sigtypeNEOPIXEL: + { // redpin,amberpin,greenpin are the 3 RG values but with no blue permitted. . (code limitation hack) - int colour_RG=redpin; - if (rag==SIGNAL_AMBER) colour_RG=amberpin; - if (rag==SIGNAL_GREEN) colour_RG=greenpin; - IODevice::writeAnalogue(sigid, colour_RG,true,0); + auto colour_RG=signal.redpin; + if (rag==SIGNAL_AMBER) colour_RG=signal.amberpin; + if (rag==SIGNAL_GREEN) colour_RG=signal.greenpin; + + // blue channel is in followng signal slot (a continuation) + auto signal2=getSignalSlot(sigslot+1); + auto colour_B=signal2.redpin; + if (rag==SIGNAL_AMBER) colour_B=signal2.amberpin; + if (rag==SIGNAL_GREEN) colour_B=signal2.greenpin; + IODevice::writeAnalogue(id, colour_RG,true,colour_B); return; } - - + + case sigtypeSIGNAL: + case sigtypeSIGNALH: + { // LED or similar 3 pin signal, (all pins zero would be a virtual signal) // If amberpin is zero, synthesise amber from red+green const byte SIMAMBER=0x00; - if (rag==SIGNAL_AMBER && (amberpin==0)) rag=SIMAMBER; // special case this func only + if (rag==SIGNAL_AMBER && (signal.amberpin==0)) rag=SIMAMBER; // special case this func only // Manage invert (HIGH on) pins - bool aHigh=sighandle & ACTIVE_HIGH_SIGNAL_FLAG; + bool aHigh=signal.type==sigtypeSIGNALH; // set the three pins - if (redpin) { + if (signal.redpin) { bool redval=(rag==SIGNAL_RED || rag==SIMAMBER); if (!aHigh) redval=!redval; - killBlinkOnVpin(redpin); - IODevice::write(redpin,redval); + killBlinkOnVpin(signal.redpin); + IODevice::write(signal.redpin,redval); } - if (amberpin) { + if (signal.amberpin) { bool amberval=(rag==SIGNAL_AMBER); if (!aHigh) amberval=!amberval; - killBlinkOnVpin(amberpin); - IODevice::write(amberpin,amberval); + killBlinkOnVpin(signal.amberpin); + IODevice::write(signal.amberpin,amberval); } - if (greenpin) { + if (signal.greenpin) { bool greenval=(rag==SIGNAL_GREEN || rag==SIMAMBER); if (!aHigh) greenval=!greenval; - killBlinkOnVpin(greenpin); - IODevice::write(greenpin,greenval); + killBlinkOnVpin(signal.greenpin); + IODevice::write(signal.greenpin,greenval); + } +} + case sigtypeVIRTUAL: break; + case sigtypeContinuation: break; + case sigtypeNoMoreSignals: break; } } /* static */ bool RMFT2::isSignal(int16_t id,char rag) { if (!(compileFeatures & FEATURE_SIGNAL)) return false; - int16_t sigslot=getSignalSlot(id); + int16_t sigslot=signalLookup->find(id); if (sigslot<0) return false; return (flags[sigslot] & SIGNAL_MASK) == rag; } @@ -1263,26 +1268,23 @@ if (sigtype== NEOPIXEL_SIGNAL_FLAG) { // Otherwise false so the parser should send the command directly bool RMFT2::signalAspectEvent(int16_t address, byte aspect ) { if (!(compileFeatures & FEATURE_SIGNAL)) return false; - int16_t sigslot=getSignalSlot(address); + auto sigslot=signalLookup->find(address); if (sigslot<0) return false; // this is not a defined signal - int16_t sigpos=sigslot*8; - int16_t sighandle=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigpos); - VPIN sigtype=sighandle & ~SIGNAL_ID_MASK; - VPIN sigid = sighandle & SIGNAL_ID_MASK; - if (sigtype!=DCCX_SIGNAL_FLAG) return false; // not a DCCX signal + auto signal=getSignalSlot(sigslot); + if (signal.type!=sigtypeDCCX) return false; // not a DCCX signal // Turn an aspect change into a RED/AMBER/GREEN setting - if (aspect==GETHIGHFLASHW(RMFT2::SignalDefinitions,sigpos+2)) { - doSignal(sigid,SIGNAL_RED); + if (aspect==signal.redpin) { + doSignal(address,SIGNAL_RED); return true; } - if (aspect==GETHIGHFLASHW(RMFT2::SignalDefinitions,sigpos+4)) { - doSignal(sigid,SIGNAL_AMBER); + if (aspect==signal.amberpin) { + doSignal(address,SIGNAL_AMBER); return true; } - if (aspect==GETHIGHFLASHW(RMFT2::SignalDefinitions,sigpos+6)) { - doSignal(sigid,SIGNAL_GREEN); + if (aspect==signal.greenpin) { + doSignal(address,SIGNAL_GREEN); return true; } diff --git a/EXRAIL2.h b/EXRAIL2.h index 1c92a2b..a816a16 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -110,6 +110,23 @@ enum BlinkState: byte { blink_high, // blink task running with pin high at_timeout // ATTIMEOUT timed out flag }; +enum SignalType { + sigtypeVIRTUAL, + sigtypeSIGNAL, + sigtypeSIGNALH, + sigtypeDCC, + sigtypeDCCX, + sigtypeSERVO, + sigtypeNEOPIXEL, + sigtypeContinuation, // neopixels require a second line + sigtypeNoMoreSignals + }; + + struct SIGNAL_DEFINITION { + SignalType type; + VPIN id; + VPIN redpin,amberpin,greenpin; + }; // Flag bits for compile time features. static const byte FEATURE_SIGNAL= 0x80; @@ -171,15 +188,7 @@ class LookList { static void rotateEvent(int16_t id, bool change); static void powerEvent(int16_t track, bool overload); static bool signalAspectEvent(int16_t address, byte aspect ); - static const int16_t SERVO_SIGNAL_FLAG=0x4000; - static const int16_t ACTIVE_HIGH_SIGNAL_FLAG=0x2000; - static const int16_t DCC_SIGNAL_FLAG=0x1000; - static const int16_t DCCX_SIGNAL_FLAG=0x3000; - static const int16_t NEOPIXEL_SIGNAL_FLAG=0x5000; - static const int16_t SIGNAL_ID_MASK=0x0FFF; - // Neopixel has last bit 1 for colour on, otherwise black (to allow for blinking etc with SET/RESET) - static const int16_t NEOPIXEL_FLAG_ON=0x0001; - // Throttle Info Access functions built by exrail macros + // Throttle Info Access functions built by exrail macros static const byte rosterNameCount; static const int16_t HIGHFLASH routeIdList[]; static const int16_t HIGHFLASH automationIdList[]; @@ -193,7 +202,8 @@ class LookList { static const FSH * getTurntablePositionDescription(int16_t turntableId, uint8_t positionId); static void startNonRecursiveTask(const FSH* reason, int16_t id,int pc); static bool readSensor(uint16_t sensorId); - static bool isSignal(int16_t id,char rag); + static bool isSignal(int16_t id,char rag); + static SIGNAL_DEFINITION getSignalSlot(int16_t slotno); private: static void ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16_t p[]); @@ -203,7 +213,6 @@ private: static bool getFlag(VPIN id,byte mask); static int16_t progtrackLocoId; static void doSignal(int16_t id,char rag); - static int16_t getSignalSlot(int16_t id); static void setTurnoutHiddenState(Turnout * t); #ifndef IO_NO_HAL static void setTurntableHiddenState(Turntable * tto); @@ -227,10 +236,11 @@ private: static bool diag; static const HIGHFLASH3 byte RouteCode[]; - static const HIGHFLASH int16_t SignalDefinitions[]; + static const HIGHFLASH SIGNAL_DEFINITION SignalDefinitions[]; static byte flags[MAX_FLAGS]; static Print * LCCSerial; static LookList * routeLookup; + static LookList * signalLookup; static LookList * onThrowLookup; static LookList * onCloseLookup; static LookList * onActivateLookup; diff --git a/EXRAIL2Parser.cpp b/EXRAIL2Parser.cpp index 8c498ab..a26d78a 100644 --- a/EXRAIL2Parser.cpp +++ b/EXRAIL2Parser.cpp @@ -252,13 +252,13 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) { // do the signals // flags[n] represents the state of the nth signal in the table for (int sigslot=0;;sigslot++) { - int16_t sighandle=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigslot*8); - if (sighandle==0) break; // end of signal list - VPIN sigid = sighandle & SIGNAL_ID_MASK; - byte flag=flags[sigslot] & SIGNAL_MASK; // obtain signal flags for this id + SIGNAL_DEFINITION slot=getSignalSlot(sigslot); + if (slot.type==sigtypeNoMoreSignals) break; // end of signal list + if (slot.type==sigtypeContinuation) continue; // continueation of previous line + byte flag=flags[sigslot] & SIGNAL_MASK; // obtain signal flags for this ids StringFormatter::send(stream,F("\n%S[%d]"), (flag == SIGNAL_RED)? F("RED") : (flag==SIGNAL_GREEN) ? F("GREEN") : F("AMBER"), - sigid); + slot.id); } } diff --git a/EXRAILMacros.h b/EXRAILMacros.h index c0e1931..7fcfc73 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -72,7 +72,7 @@ //const byte TRACK_POWER_1=1, TRACK_POWER_ON=1; // NEOPIXEL RG generator for NEOPIXEL_SIGNAL -#define NeoRG(red,green) ((red & 0xff)<<8) | (green & 0xff) +#define NeoRGB(red,green,blue) (((uint32_t)(red & 0xff)<<16) | ((uint32_t)(green & 0xff)<<8) | (uint32_t)(blue & 0xff)) // Pass 1 Implements aliases #include "EXRAIL2MacroReset.h" @@ -425,23 +425,26 @@ const FSH * RMFT2::getRosterFunctions(int16_t id) { // Pass 8 Signal definitions #include "EXRAIL2MacroReset.h" #undef SIGNAL -#define SIGNAL(redpin,amberpin,greenpin) redpin,redpin,amberpin,greenpin, +#define SIGNAL(redpin,amberpin,greenpin) {sigtypeSIGNAL,redpin,redpin,amberpin,greenpin}, #undef SIGNALH -#define SIGNALH(redpin,amberpin,greenpin) redpin | RMFT2::ACTIVE_HIGH_SIGNAL_FLAG,redpin,amberpin,greenpin, +#define SIGNALH(redpin,amberpin,greenpin) {sigtypeSIGNALH,redpin,redpin,amberpin,greenpin}, #undef SERVO_SIGNAL -#define SERVO_SIGNAL(vpin,redval,amberval,greenval) vpin | RMFT2::SERVO_SIGNAL_FLAG,redval,amberval,greenval, +#define SERVO_SIGNAL(vpin,redval,amberval,greenval) {sigtypeSERVO,vpin,redval,amberval,greenval}, #undef DCC_SIGNAL -#define DCC_SIGNAL(id,addr,subaddr) id | RMFT2::DCC_SIGNAL_FLAG,addr,subaddr,0, +#define DCC_SIGNAL(id,addr,subaddr) {sigtypeDCC,id,addr,subaddr,0}, #undef DCCX_SIGNAL -#define DCCX_SIGNAL(id,redAspect,amberAspect,greenAspect) id | RMFT2::DCCX_SIGNAL_FLAG,redAspect,amberAspect,greenAspect, +#define DCCX_SIGNAL(id,redAspect,amberAspect,greenAspect) {sigtypeDCCX,id,redAspect,amberAspect,greenAspect}, #undef NEOPIXEL_SIGNAL -#define NEOPIXEL_SIGNAL(id,redcolour,ambercolour,greencolour) id | RMFT2::NEOPIXEL_SIGNAL_FLAG,redcolour, ambercolour, greencolour, +#define NEOPIXEL_SIGNAL(id,redRGB,amberRGB,greenRGB) \ + {sigtypeNEOPIXEL,id,((VPIN)((redRGB)>>8)), ((VPIN)((amberRGB)>>8)), ((VPIN)((greenRGB)>>8))},\ + {sigtypeContinuation,id,((VPIN)((redRGB) & 0xff)), ((VPIN)((amberRGB) & 0xFF)), ((VPIN)((greenRGB) & 0xFF))}, #undef VIRTUAL_SIGNAL -#define VIRTUAL_SIGNAL(id) id,0,0,0, +#define VIRTUAL_SIGNAL(id) {sigtypeVIRTUAL,id,0,0,0}, -const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { +const HIGHFLASH SIGNAL_DEFINITION RMFT2::SignalDefinitions[] = { #include "myAutomation.h" - 0,0,0,0 }; + {sigtypeNoMoreSignals,0,0,0,0} + }; // Pass 9 ONLCC/ ONMERG counter and lookup array #include "EXRAIL2MacroReset.h" diff --git a/FSH.h b/FSH.h index 280d37e..cdda579 100644 --- a/FSH.h +++ b/FSH.h @@ -60,6 +60,8 @@ typedef __FlashStringHelper FSH; #define GETFARPTR(data) pgm_get_far_address(data) #define GETHIGHFLASH(data,offset) pgm_read_byte_far(GETFARPTR(data)+offset) #define GETHIGHFLASHW(data,offset) pgm_read_word_far(GETFARPTR(data)+offset) +#define COPYHIGHFLASH(target,base,offset,length) \ + memcpy_PF(target,GETFARPTR(base) + offset,length) #else // AVR_UNO/NANO runtime does not support _far functions so just use _near equivalent // as there is no progmem above 32kb anyway. @@ -68,6 +70,8 @@ typedef __FlashStringHelper FSH; #define GETFARPTR(data) ((uint32_t)(data)) #define GETHIGHFLASH(data,offset) pgm_read_byte_near(GETFARPTR(data)+(offset)) #define GETHIGHFLASHW(data,offset) pgm_read_word_near(GETFARPTR(data)+(offset)) +#define COPYHIGHFLASH(target,base,offset,length) \ + memcpy_P(target,(byte *)base + offset,length) #endif #else @@ -87,6 +91,8 @@ typedef char FSH; #define GETFLASH(addr) (*(const byte *)(addr)) #define GETHIGHFLASH(data,offset) (*(const byte *)(GETFARPTR(data)+offset)) #define GETHIGHFLASHW(data,offset) (*(const uint16_t *)(GETFARPTR(data)+offset)) +#define COPYHIGHFLASH(target,base,offset,length) \ + memcpy(target,(byte *)&base + offset,length) #define STRCPY_P strcpy #define STRCMP_P strcmp #define STRNCPY_P strncpy diff --git a/Release_Notes/NeoPixel.md b/Release_Notes/NeoPixel.md index e0bfc22..6d38852 100644 --- a/Release_Notes/NeoPixel.md +++ b/Release_Notes/NeoPixel.md @@ -54,8 +54,24 @@ The IO_NeoPixel.h driver supports the adafruit neopixel seesaw board. It turns e For signals with 1 pixel, the NEOPIXEL_SIGNAL macro will create a signal NEOPIXEL_SIGNAL(vpin,redfx,amberfx,greenfx) - *** This is experimental and may change**** - In order to fit the existing signal code, the fx colours above are restricted to the red and green pixel values (ie no blue channel) - The fx values above can be created by the NeoRG macro so a bright red would be NeoRG(255,0) bright green Ng(0,255) and amber something like NeoRG(128,128) + ** Changed... **** + The fx values above can be created by the NeoRGB macro so a bright red would be NeoRGB(255,0,0) bright green NeoRGB(0,255,0) and amber something like NeoRGB(255,100,0) + NeoRGB creates a single int32_t value so it can be used in several ways as convenient. + +// create 1-lamp signal with NeoRGB colours +NEOPIXEL_SIGNAL(1000,NeoRGB(255,0,0),NeoRGB(255,100,0),NeoRGB(0,255,0)) + +// Create 1-lamp signal with named colours. +// This is better if you have multiple signals. +// (Note: ALIAS is not suitable due to word length defaults) +#define REDLAMP NeoRGB(255,0,0) +#define AMBERLAMP NeoRGB(255,100,0) +#define GREENLAMP NeoRGB(0,255,0) +NEOPIXEL_SIGNAL(1001,REDLAMP,AMBERLAMP,GREENLAMP) + +// Create 1-lamp signal with web type RGB colours +// (Using blue for the amber signal , just testing) +NEOPIXEL_SIGNAL(1002,0xFF0000,0x0000FF,0x00FF00) + \ No newline at end of file