diff --git a/DCC.cpp b/DCC.cpp index dad0c2b..4a1cedd 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -389,6 +389,25 @@ void DCC::writeCVByteMain(int cab, int cv, byte bValue) { DCCWaveform::mainTrack.schedulePacket(b, nB, 4); } +// +// readCVByteMain: Read a byte with PoM on main. +// This requires Railcom active +// +void DCC::readCVByteMain(int cab, int cv, ACK_CALLBACK callback) { + byte b[5]; + byte nB = 0; + if (cab > HIGHEST_SHORT_ADDR) + b[nB++] = highByte(cab) | 0xC0; // convert train number into a two-byte address + + b[nB++] = lowByte(cab); + b[nB++] = cv1(READ_BYTE_MAIN, cv); // any CV>1023 will become modulus(1024) due to bit-mask of 0x03 + b[nB++] = cv2(cv); + b[nB++] = 0; + + DCCWaveform::mainTrack.schedulePacket(b, nB, 4); + Railcom::anticipate(cab,cv,callback); +} + // // writeCVBitMain: Write a bit of a byte with PoM on main. This writes // the 5 byte sized packet to implement this DCC function diff --git a/DCC.h b/DCC.h index 7993847..e6e4965 100644 --- a/DCC.h +++ b/DCC.h @@ -64,6 +64,8 @@ public: static uint8_t getThrottleFrequency(int cab); static bool getThrottleDirection(int cab); static void writeCVByteMain(int cab, int cv, byte bValue); + static void readCVByteMain(int cab, int cv, ACK_CALLBACK callback); + static void writeCVBitMain(int cab, int cv, byte bNum, bool bValue); static void setFunction(int cab, byte fByte, byte eByte); static bool setFn(int cab, int16_t functionNumber, bool on); @@ -128,6 +130,7 @@ private: // NMRA codes # static const byte SET_SPEED = 0x3f; static const byte WRITE_BYTE_MAIN = 0xEC; + static const byte READ_BYTE_MAIN = 0xE4; static const byte WRITE_BIT_MAIN = 0xE8; static const byte WRITE_BYTE = 0x7C; static const byte VERIFY_BYTE = 0x74; diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 0bd0ecb..0225c47 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -480,6 +480,14 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) break; DCC::writeCVByteMain(p[0], p[1], p[2]); return; + + case 'r': // READ CV on MAIN Requires Railcom + if (params != 2) + break; + if (!DCCWaveform::isRailcom()) break; + if (!stashCallback(stream, p, ringStream)) break; + DCC::readCVByteMain(p[0], p[1],callback_r); + return; case 'b': // WRITE CV BIT ON MAIN if (params != 4) @@ -1417,6 +1425,12 @@ void DCCEXParser::callback_R(int16_t result) commitAsyncReplyStream(); } +void DCCEXParser::callback_r(int16_t result) +{ + StringFormatter::send(getAsyncReplyStream(), F("\n"), stashP[0], stashP[1], result); + commitAsyncReplyStream(); +} + void DCCEXParser::callback_Rloco(int16_t result) { const FSH * detail; if (result<=0) { diff --git a/DCCEXParser.h b/DCCEXParser.h index b9d56ea..2b38e2a 100644 --- a/DCCEXParser.h +++ b/DCCEXParser.h @@ -68,7 +68,8 @@ struct DCCEXParser static void callback_W(int16_t result); static void callback_W4(int16_t result); static void callback_B(int16_t result); - static void callback_R(int16_t result); + static void callback_R(int16_t result); // prog + static void callback_r(int16_t result); // main static void callback_Rloco(int16_t result); static void callback_Wloco(int16_t result); static void callback_Wconsist(int16_t result); diff --git a/Railcom.cpp b/Railcom.cpp index aef6bd5..1b9225c 100644 --- a/Railcom.cpp +++ b/Railcom.cpp @@ -145,6 +145,10 @@ Railcom::Railcom(uint16_t blockvpin) { lastChannel1Loco=0; vpin=blockvpin; } +uint16_t Railcom::expectLoco=0; +uint16_t Railcom::expectCV=0; +uint16_t Railcom::expectWait=0; +ACK_CALLBACK Railcom::expectCallback=0; // Process is called by a raw data collector. @@ -170,6 +174,30 @@ void Railcom::process(uint8_t * inbound, uint8_t length) { } } + if (expectCV && DCCWaveform::getRailcomLastLocoAddress()==expectLoco) { + if (length>=4) { + auto v2=GETHIGHFLASH(decode,inbound[2]); + auto v3=GETHIGHFLASH(decode,inbound[3]); + uint16_t packet=(v2<<6) | (v3 & 0x3f); + // packet is 12 bits TTTTDDDDDDDD + byte type=(packet>>8) & 0x0F; + byte data= packet & 0xFF; + if (type==RMOB_POM) { + DIAG(F("POM READ loco=%d cv(%d)=%d/0x%x"), expectLoco, expectCV,data,data); + expectCallback(data); + expectCV=0; + } + } + if (expectCV) { // still waiting + expectWait--; + if (expectWait==0) { + DIAG(F("POM READ loco=%d cv(%d) FAIL"), expectLoco, expectCV); + expectCallback(-1); + expectCV=0; + } + } + + } auto v1=GETHIGHFLASH(decode,inbound[0]); auto v2=(length>1) ? GETHIGHFLASH(decode,inbound[1]):INV; diff --git a/Railcom.h b/Railcom.h index 1207b29..63487ea 100644 --- a/Railcom.h +++ b/Railcom.h @@ -22,6 +22,8 @@ #define Railcom_h #include "Arduino.h" +typedef void (*ACK_CALLBACK)(int16_t result); + class Railcom { public: Railcom(uint16_t vpin); @@ -31,8 +33,16 @@ class Railcom { >0: loco id */ void process(uint8_t * inbound,uint8_t length); + static void anticipate(uint16_t loco, uint16_t cv, ACK_CALLBACK callback) { + expectLoco=loco; + expectCV=cv; + expectWait=10; // channel3 packets + expectCallback=callback; + }; private: + static uint16_t expectCV,expectLoco,expectWait; + static ACK_CALLBACK expectCallback; void noData(); uint16_t vpin; uint8_t holdoverHigh,holdoverLow;