From 07f641c0b5beaec75fceda063ef96502d41feacb Mon Sep 17 00:00:00 2001 From: sle Date: Wed, 10 Mar 2021 12:42:30 +0100 Subject: [PATCH] fix #11047 Wiegand 26/34 missed some key press --- tasmota/xsns_82_wiegand.ino | 265 +++++++++++++++++------------------- 1 file changed, 128 insertions(+), 137 deletions(-) diff --git a/tasmota/xsns_82_wiegand.ino b/tasmota/xsns_82_wiegand.ino index b0607ef07..e99fd43db 100644 --- a/tasmota/xsns_82_wiegand.ino +++ b/tasmota/xsns_82_wiegand.ino @@ -36,22 +36,27 @@ * * Rule: * on wiegand#uid=4302741608 do publish cmnd/ailight/power 2 endon + * + * contains: + * - fix for #11047 Wiegand 26/34 missed some key press if they are press at normal speed + * - removed testing code for tests without attached hardware \*********************************************************************************************/ #warning **** Wiegand interface enabled **** #define XSNS_82 82 -#define WIEGAND_BIT_TIMEOUT 25 // Time in mSec to be wait after last bit detected. +#define WIEGAND_CODE_GAP_FACTOR 3 // Gap between 2 complete RFID codes send by the device. (WIEGAND_CODE_GAP_FACTOR * bitTime) to detect the end of a code +#define WIEGAND_BIT_TIME_DEFAULT 1250 // period time of one bit (impluse + impulse_gap time) 1250µs measured by oscilloscope on my RFID Reader +#define WIEGAND_RFID_ARRAY_SIZE 5 // storage of rfids found between 2 calls of FUNC_EVERY_100_MSECOND -// Use only a randomly generate RFID for testing. using #define will save some space in the final code -// DEV_WIEGAND_TEST_MODE 1 : testing with random rfid without hardware connected, but GPIOs set correctly +// using #define will save some space in the final code // DEV_WIEGAND_TEST_MODE 2 : testing with hardware correctly connected. #define DEV_WIEGAND_TEST_MODE 0 #ifdef DEV_WIEGAND_TEST_MODE #if (DEV_WIEGAND_TEST_MODE==0) #elif (DEV_WIEGAND_TEST_MODE==1) - #warning "Wiegand Interface compiled with 'DEV_WIEGAND_TEST_MODE' 1 (Random RFID)" + #warning "(no longer available) Wiegand Interface compiled with 'DEV_WIEGAND_TEST_MODE' 1 (Random RFID)" #elif (DEV_WIEGAND_TEST_MODE==2) #warning "Wiegand Interface compiled with 'DEV_WIEGAND_TEST_MODE' 2 (Hardware connected)" #else @@ -59,6 +64,8 @@ #endif #endif +typedef struct rfid_store { uint64_t RFID; uint16_t bitCount; } RFID_store; + class Wiegand { public: @@ -70,17 +77,17 @@ class Wiegand { #endif // USE_WEBSERVER bool isInit = false; - uint8_t scanDelay; private: - uint64_t HexStringToDec(uint64_t); uint64_t CheckAndConvertRfid(uint64_t,uint16_t); char translateEnterEscapeKeyPress(char); uint8_t CalculateParities(uint64_t, int); - bool WiegandConversion (void); + //bool WiegandConversion (void); + bool WiegandConversion (uint64_t , uint16_t ); static void handleD0Interrupt(void); static void handleD1Interrupt(void); + static void handleDxInterrupt(int in); // fix #11047 uint64_t rfid; uint8_t tagSize; @@ -88,11 +95,14 @@ class Wiegand { static volatile uint64_t rfidBuffer; static volatile uint16_t bitCount; static volatile uint32_t lastFoundTime; - static volatile uint8_t timeOut; - -#if (DEV_WIEGAND_TEST_MODE)==1 - uint64_t GetRandomRfid(uint8_t); -#endif // DEV_WIEGAND_TEST_MODE==1 + // fix #11047 + static volatile uint32_t bitTime; + static volatile uint32_t FirstBitTimeStamp; + static volatile uint32_t CodeGapTime; + static volatile bool CodeComplete; + static volatile RFID_store rfid_found[]; + static volatile int currentFoundRFIDcount; + }; Wiegand* oWiegand = new Wiegand(); @@ -100,7 +110,13 @@ Wiegand* oWiegand = new Wiegand(); volatile uint64_t Wiegand::rfidBuffer; volatile uint16_t Wiegand::bitCount; volatile uint32_t Wiegand::lastFoundTime; -volatile uint8_t Wiegand::timeOut; +// fix for #11047 +volatile uint32_t Wiegand::bitTime; +volatile uint32_t Wiegand::FirstBitTimeStamp; +volatile uint32_t Wiegand::CodeGapTime; +volatile bool Wiegand::CodeComplete; +volatile RFID_store Wiegand::rfid_found[WIEGAND_RFID_ARRAY_SIZE]; +volatile int Wiegand::currentFoundRFIDcount; Wiegand::Wiegand() { rfid = 0; @@ -108,73 +124,68 @@ Wiegand::Wiegand() { tagSize = 0; rfidBuffer = 0; bitCount = 0 ; - timeOut = 0; isInit = false; - scanDelay = 1; -} - -#if (DEV_WIEGAND_TEST_MODE)==1 -uint64_t Wiegand::GetRandomRfid(uint8_t tag_size=34) { - // Todo add support for 4 and 8 bit keyboard "tags" - uint64_t result = (uint32_t)HwRandom(); - uint8_t parities = 0; - bitCount = tag_size; - timeOut = millis() - WIEGAND_BIT_TIMEOUT; - result = result << 32; - result += HwRandom(); - - switch (tag_size){ - case 24: - result = (result & 0x7FFFFE) >>1; - break; - case 26: - result = (result & 0x1FFFFFE) >>1; - break; - case 32: - result = (result & 0x7FFFFFFE) >>1; - break; - case 34: - result = (result & 0x3FFFFFFFE) >>1; - break; - default: - break; + // fix #11047 + bitTime = WIEGAND_BIT_TIME_DEFAULT; + FirstBitTimeStamp = 0; + CodeGapTime = WIEGAND_CODE_GAP_FACTOR * bitTime; + CodeComplete = false; + currentFoundRFIDcount=0; + for (int i=0; i < WIEGAND_RFID_ARRAY_SIZE; i++ ) + { + rfid_found[i].RFID=0; + rfid_found[i].bitCount=0; } - parities = CalculateParities(result, tag_size); - - result = (result << 1) | (parities & 0x01); // Set LSB parity - if (parities & 0x80) { // MSB parity is 1 - switch (tag_size) { - case 24: - result |= 0x800000; - break; - case 26: - result |= 0x2000000; - break; - case 32: - result |= 0x80000000; - break; - case 34: - result |= 0x400000000; - break; - default: - break; - } - } - - return result; } -#endif // DEV_WIEGAND_TEST_MODE==1 void ICACHE_RAM_ATTR Wiegand::handleD1Interrupt() { // Receive a 1 bit. (D0=high & D1=low) - rfidBuffer = (rfidBuffer << 1) | 1; // Leftshift + 1 bit - bitCount++; // Increment the counter - lastFoundTime = millis(); // Last time bit found + handleDxInterrupt(1); } void ICACHE_RAM_ATTR Wiegand::handleD0Interrupt() { // Receive a 0 bit. (D0=low & D1=high) - rfidBuffer = rfidBuffer << 1; // Leftshift the 0 bit is now at the end of rfidBuffer - bitCount++; // Increment the counter - lastFoundTime = millis(); // Last time bit found + handleDxInterrupt(0); +} + +void ICACHE_RAM_ATTR Wiegand::handleDxInterrupt(int in) { + + unsigned long curTime = micros(); // to be sure I will use micros() instead of millis() overflow is handle by using the minus operator to compare + unsigned long diffTime= curTime - lastFoundTime; + if (diffTime > 3000000 ) { //cancel noisy bits in buffer and start a new tag + rfidBuffer = 0; + bitCount = 0; + FirstBitTimeStamp = 0; + } + if ( (diffTime > CodeGapTime) && (bitCount > 0)) { + // previous RFID tag (key pad numer)is complete. Will be detected by the code ending gap + // one bit will take the time of impulse_time + impulse_gap_time. it (bitTime) will be recalculated each time an impulse is detected + // the devices will add some inter_code_gap_time to separate codes this will be much longer than the bit_time. (WIEGAND_CODE_GAP_FACTOR) + // unfortunately there's no timing defined for Wiegang. On my test reader the impulse time = 125 µs impulse gap time = 950 µs. + if (currentFoundRFIDcount < WIEGAND_RFID_ARRAY_SIZE) { // when reaching the end of rfid buffer we will overwrite the last one. + currentFoundRFIDcount++; + } + // start a new tag + rfidBuffer = 0; + bitCount = 0; + FirstBitTimeStamp = 0; + } + if (in ==3) {// called by ScanForTag to get the last tag, because the interrupt handler is no longer called after receiving the last bit + return; + } + if (in == 0) { rfidBuffer = rfidBuffer << 1; } // Receive a 0 bit. (D0=low & D1=high): Leftshift the 0 bit is now at the end of rfidBuffer + else {rfidBuffer = (rfidBuffer << 1) | 1; } // Receive a 1 bit. (D0=high & D1=low): Leftshift + 1 bit + + bitCount++; + if (bitCount == 1) { // first bit was detected + FirstBitTimeStamp = (curTime != 0) ? curTime : 1; // accept 1µs differenct to avoid a miss the first timestamp if curTime is 0. + } + else if (bitCount == 2) { // only calculate once per RFID tag + bitTime = diffTime; //calc maximum current length of one bit + CodeGapTime = WIEGAND_CODE_GAP_FACTOR * bitTime; + } + //save current rfid in array otherwise we will never see the last found tag + rfid_found[currentFoundRFIDcount].RFID=rfidBuffer; + rfid_found[currentFoundRFIDcount].bitCount= bitCount; + lastFoundTime = curTime; // Last time a bit was detected } void Wiegand::Init() { @@ -201,11 +212,11 @@ void Wiegand::Init() { #endif // DEV_WIEGAND_TEST_MODE>0 } -uint64_t Wiegand::CheckAndConvertRfid(uint64_t rfidIn, uint16_t bitcount) { +uint64_t Wiegand::CheckAndConvertRfid(uint64_t rfidIn, uint16_t bitCount) { uint8_t evenParityBit = 0; uint8_t oddParityBit = (uint8_t) (rfidIn & 0x1); // Last bit = odd parity uint8_t calcParity = 0; - switch (bitcount) { + switch (bitCount) { case 24: evenParityBit = (rfidIn & 0x800000) ? 0x80 : 0; rfidIn = (rfidIn & 0x7FFFFE) >>1; @@ -282,18 +293,14 @@ char Wiegand::translateEnterEscapeKeyPress(char oKeyPressed) { } } -bool Wiegand::WiegandConversion () { +bool Wiegand::WiegandConversion (uint64_t rfidBuffer, uint16_t bitCount) { bool bRet = false; - unsigned long nowTick = millis(); + // unsigned long nowTick = micros(); // Add a maximum wait time for new bits - unsigned long diffTicks = nowTick - lastFoundTime; - if ((diffTicks > WIEGAND_BIT_TIMEOUT) && (diffTicks >= 5000 )) { // Max. 5 secs between 2 bits comming in - bitCount = 0; - rfidBuffer = 0; - lastFoundTime = nowTick; - return bRet; - } - if (diffTicks > WIEGAND_BIT_TIMEOUT) { // Last bit found is WIEGAND_BIT_TIMEOUT ms ago + // unsigned long diffTicks = nowTick - lastFoundTime; + // unsigned long inter_code_gap = WIEGAND_CODE_GAP_FACTOR * bitTime; + // if ((diffTicks > inter_code_gap) && (diffTicks >= 1000000 )) { // Max. 4-8 secs between 2 bits comming in. depends on micros() resolution + #if (DEV_WIEGAND_TEST_MODE)>0 AddLog(LOG_LEVEL_INFO, PSTR("WIE: Raw tag %llu, Bit count %u"), rfidBuffer, bitCount); #endif // DEV_WIEGAND_TEST_MODE>0 @@ -319,20 +326,15 @@ bool Wiegand::WiegandConversion () { rfid = (int)translateEnterEscapeKeyPress(lowNibble); bRet = true; } else { - lastFoundTime = nowTick; + // lastFoundTime = nowTick; bRet = false; } tagSize = bitCount; } else { // Time reached but unknown bitCount, clear and start again - lastFoundTime = nowTick; + // lastFoundTime = nowTick; bRet = false; } - bitCount = 0; - rfidBuffer = 0; - } else { - bRet = false; // Watching time not finished - } #if (DEV_WIEGAND_TEST_MODE)>0 AddLog(LOG_LEVEL_INFO, PSTR("WIE: Tag out %llu, tag size %u "), rfid, tagSize); #endif // DEV_WIEGAND_TEST_MODE>0 @@ -340,43 +342,42 @@ bool Wiegand::WiegandConversion () { } void Wiegand::ScanForTag() { -#if (DEV_WIEGAND_TEST_MODE)>0 - AddLog(LOG_LEVEL_INFO, PSTR("WIE: ScanForTag().")); -#if (DEV_WIEGAND_TEST_MODE==1) - switch (millis() %4) { - case 0: - rfidBuffer = GetRandomRfid(24); - break; - case 1: - rfidBuffer = GetRandomRfid(26); - break; - case 2: - rfidBuffer = GetRandomRfid(32); - break; - case 3: - rfidBuffer = GetRandomRfid(34); - break; - default: - rfidBuffer = GetRandomRfid(34); - break; - } - AddLog(LOG_LEVEL_INFO, PSTR("WIE: Raw generated %lX"), rfidBuffer); // For tests without reader attaiched -#endif // DEV_WIEGAND_TEST_MODE==1 -#endif // DEV_WIEGAND_TEST_MODE>0 - if (bitCount > 0) { - uint64_t oldTag = rfid; - bool validKey = WiegandConversion(); -#if (DEV_WIEGAND_TEST_MODE)>0 - AddLog(LOG_LEVEL_INFO, PSTR("WIE: Previous tag %llu"), oldTag); -#endif // DEV_WIEGAND_TEST_MODE>0 - if (validKey) { // Only in case of valid key do action. Issue#10585 - if (oldTag == rfid) { - AddLog(LOG_LEVEL_DEBUG, PSTR("WIE: Old tag")); + unsigned long startTime = micros(); + handleDxInterrupt(3); + if (currentFoundRFIDcount > 0) { + unsigned int lastFoundRFIDcount = currentFoundRFIDcount; + #if (DEV_WIEGAND_TEST_MODE)>0 + AddLog(LOG_LEVEL_INFO, PSTR("WIE: ScanForTag(). bitTime: %0lu lastFoundTime: %0lu RFIDS in buffer: %lu"), bitTime, lastFoundTime, currentFoundRFIDcount); + #endif + for (int i= 0; i < WIEGAND_RFID_ARRAY_SIZE; i++) + { + if (rfid_found[i].RFID != 0) { + uint64_t oldTag = rfid; + bool validKey = WiegandConversion(rfid_found[i].RFID, rfid_found[i].bitCount); + #if (DEV_WIEGAND_TEST_MODE)>0 + AddLog(LOG_LEVEL_INFO, PSTR("WIE: Previous tag %llu"), oldTag); + #endif // DEV_WIEGAND_TEST_MODE>0 + if (validKey) { // Only in case of valid key do action. Issue#10585 + if (oldTag == rfid) { + AddLog(LOG_LEVEL_DEBUG, PSTR("WIE: Old tag")); + } + ResponseTime_P(PSTR(",\"Wiegand\":{\"UID\":%0llu,\"" D_JSON_SIZE "\":%u}}"), rfid,tagSize); + MqttPublishTeleSensor(); + } + rfid_found[i].RFID=0; + rfid_found[i].bitCount=0; } - ResponseTime_P(PSTR(",\"Wiegand\":{\"UID\":%0llu,\"" D_JSON_SIZE "\":%u}}"), rfid, tagSize); - MqttPublishTeleSensor(); } + if (currentFoundRFIDcount > lastFoundRFIDcount) { + // if that happens: we need to move the id found during the loop to top of the array + // and correct the currentFoundRFIDcount + } + currentFoundRFIDcount=0; //reset array + #if (DEV_WIEGAND_TEST_MODE)>0 + AddLog(LOG_LEVEL_INFO, PSTR("WIE: ScanForTag() time elapsed %lu"), (micros() - startTime)); + #endif } + } #ifdef USE_WEBSERVER @@ -400,18 +401,8 @@ bool Xsns82(byte function) { } else if (oWiegand->isInit) { switch (function) { - case FUNC_EVERY_250_MSECOND: // Some tags need more time, don't try shorter period -#if (DEV_WIEGAND_TEST_MODE)==1 - if (oWiegand->scanDelay >= 4) // Give a second because of the log entries to be send. -#else - if (oWiegand->scanDelay >= 2) // Only run every (delay * 250 ms) (every 250ms is too fast for some tags) -#endif - { - oWiegand->ScanForTag(); - oWiegand->scanDelay = 1; - } else { - oWiegand->scanDelay++; - } + case FUNC_EVERY_100_MSECOND: // fix for #11047 Wiegand 26/34 missed some key press + oWiegand->ScanForTag(); break; #ifdef USE_WEBSERVER case FUNC_WEB_SENSOR: