2021-01-16 14:39:33 +00:00
/*
2021-01-23 16:10:06 +00:00
xsns_82_wiegand . ino - Support for Wiegand Interface 125 kHz Rfid Tag Reader for Tasmota
2021-01-16 14:39:33 +00:00
Copyright ( C ) 2021 Sigurd Leuther and Theo Arends
This program 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 .
This program 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 this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# ifdef USE_WIEGAND
/*********************************************************************************************\
2021-01-21 12:44:39 +00:00
* Wiegand 24 , 26 , 32 , 34 bit Rfid reader 125 kHz
*
2021-01-21 15:12:44 +00:00
* Wire connections for https : //www.benselectronics.nl/wiegand-2634-bit-rfid-reader-125khze248d29925d602d.html
* Red Vdc
* Black Gnd
* Green D0
* White D1
* Yellow / Grey Sound Buzzer if connected to Gnd
* Blue Light Led if connected to Gnd
* Grey / Purple 34 - bit if connected to Gnd
2021-01-21 12:44:39 +00:00
*
* MQTT :
2021-01-21 15:12:44 +00:00
* % prefix % / % topic % / SENSOR = { " Time " : " 2021-01-21T16:04:12 " , " Wiegand " : { " UID " : 7748328 , " Size " : 26 } }
* % prefix % / % topic % / SENSOR = { " Time " : " 2021-01-21T15:48:49 " , " Wiegand " : { " UID " : 4302741608 , " Size " : 34 } }
*
* Rule :
* on wiegand # uid = 4302741608 do publish cmnd / ailight / power 2 endon
2022-02-14 17:29:26 +00:00
*
2022-02-15 13:32:42 +00:00
* 20220215
* - fix 34 - bit size parity chk
* - fix 64 - bit representation after removal of % llu support ( Tasmota does not support 64 - bit decimal output specifier ( % llu ) saving 60 k code )
* - - -
* 20201101
2022-02-14 17:29:26 +00:00
* - 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
* - added SetOption123 0 - Wiegand UID decimal ( default ) 1 - Wiegand UID hexadecimal
* - added SetOption124 0 - all keys up to ending char ( # or * ) send as one tag by MQTT ( default ) 1 - Keypad every key a single tag
2021-03-16 17:58:31 +00:00
* - added a new realtime testing option emulating a Wiegang reader output on same GPIOs where normally reader is attached . Details below
2021-03-20 16:15:42 +00:00
* - fix timing issue when fast glitches are detected on one on the datalines . The interbitgab was too short in that case
2021-01-16 14:39:33 +00:00
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2021-03-16 17:58:31 +00:00
# pragma message("**** Wiegand interface enabled ****")
2021-01-16 14:39:33 +00:00
# define XSNS_82 82
2022-02-14 17:29:26 +00:00
# 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
2021-03-16 17:58:31 +00:00
# define WIEGAND_BIT_TIME_DEFAULT 1250 // period time (µs) of one bit (impluse + impulse_gap time) 1250µs measured by oscilloscope on my RFID Reader
# define WIEGAND_RFID_ARRAY_SIZE 11 // storage of rfids found between 2 calls of FUNC_EVERY_100_MSECOND
2021-03-11 11:33:35 +00:00
# define WIEGAND_OPTION_HEX 123 // Index of option to switch output between hex (1) an decimal (0) (default)
2021-03-16 17:58:31 +00:00
# define WIEGAND_OPTION_HEX_POSTFIX "h" // will be added after UID output nothing = ""
2022-02-14 17:29:26 +00:00
# define WIEGAND_OPTION_KEYPAD_TO_TAG 124 //Index of option to switch output of key pad strokes between every single stroke one single char (0) (default)
2021-03-16 17:58:31 +00:00
// or all strokes until detecting ending char (WIEGAND_OPTION_KEYPAD_END_CHAR) as one tag (1)
2021-01-17 16:50:58 +00:00
2021-03-16 17:58:31 +00:00
# define DEV_WIEGAND_TEST_MODE 0
2021-03-10 11:42:30 +00:00
// using #define will save some space in the final code
2021-03-16 17:58:31 +00:00
// DEV_WIEGAND_TEST_MODE 1 : Use only without Wiegand reader device attache. On a second ESP to simulate reader output!
2021-01-21 15:12:44 +00:00
// DEV_WIEGAND_TEST_MODE 2 : testing with hardware correctly connected.
2021-01-16 14:39:33 +00:00
# ifdef DEV_WIEGAND_TEST_MODE
2021-01-17 16:50:58 +00:00
# if (DEV_WIEGAND_TEST_MODE==0)
# elif (DEV_WIEGAND_TEST_MODE==1)
2021-03-16 17:58:31 +00:00
# pragma message("\nWiegand Interface code generator (testing purpose only!) compiled with 'DEV_WIEGAND_TEST_MODE' 1 \nUse only on esp WITHOUT Wiegand reader hardware attached! GPIOS will be configured as OUTPUT!" )
// use on own risk for testing purpose only.
// please don't attach your reader to the ESP when you use this option. The GPIOS will be defined as OUTPUT
// the interrupts will be enabled and normally recognize the generated code, that's the idea behind for testing.
// Commands:
// WieBitTime [time] : get or set the bit impuls length
// WieInterBitTime [time]: get or set the length of the gap between 2 bits
// WieTagGap [tagGap]: get or set the current used gap time between 2 tags send in µs minimal WIEGAND_BIT_TIME_DEFAULT µs default WIEGAND_BIT_TIME_DEFAULT * WIEGAND_CODE_GAP_FACTOR
2022-02-14 17:29:26 +00:00
// WieTagSize [tagsize]: get or set the tagsize (4,8,24,26,32,34) default 26.
2021-03-16 17:58:31 +00:00
// WieTag [tag]: get or set the current used tag. For tagsize 4,8 only one char will be used.
// WieSend [tag[:tagsize];tag[:tagsize];...] : Generate the current Tag with current TagSize to GPIOs if the paramters are used
// tags and tagsize from commandline are used as current values. If tagsize is omitted always last value will be used
// WieSend 4:4;5:8; will send 4 in 4 bit mode and 5 in 8 bit mode with a pause of current TagGab between the chars
// WieSend will send the last used tag with last used tagsize
2021-01-17 16:50:58 +00:00
# elif (DEV_WIEGAND_TEST_MODE==2)
2021-03-16 17:58:31 +00:00
# pragma message("\nWiegand Interface compiled with 'DEV_WIEGAND_TEST_MODE' 2 (Hardware connected)")
2021-01-17 16:50:58 +00:00
# else
2021-03-16 17:58:31 +00:00
# pragma message("\nWiegand Interface compiled with unknown mode")
2021-01-17 16:50:58 +00:00
# endif
2021-01-16 14:39:33 +00:00
# endif
2021-03-10 11:42:30 +00:00
typedef struct rfid_store { uint64_t RFID ; uint16_t bitCount ; } RFID_store ;
2021-01-16 14:39:33 +00:00
class Wiegand {
public :
Wiegand ( void ) ;
void Init ( void ) ;
void ScanForTag ( void ) ;
# ifdef USE_WEBSERVER
void Show ( void ) ;
2021-01-21 15:12:44 +00:00
# endif // USE_WEBSERVER
bool isInit = false ;
2021-01-16 14:39:33 +00:00
2021-03-16 17:58:31 +00:00
# if (DEV_WIEGAND_TEST_MODE!=1)
private :
# endif //(DEV_WIEGAND_TEST_MODE==1)
2021-01-16 14:39:33 +00:00
uint64_t CheckAndConvertRfid ( uint64_t , uint16_t ) ;
uint8_t CalculateParities ( uint64_t , int ) ;
2021-03-10 11:42:30 +00:00
bool WiegandConversion ( uint64_t , uint16_t ) ;
2021-03-11 11:33:35 +00:00
void setOutputFormat ( void ) ; // fix output HEX format
2021-03-16 17:58:31 +00:00
void HandleKeyPad ( void ) ; //handle one tag for multi key strokes
2022-02-14 17:29:26 +00:00
2021-03-11 11:33:35 +00:00
2021-01-16 14:39:33 +00:00
static void handleD0Interrupt ( void ) ;
static void handleD1Interrupt ( void ) ;
2022-02-14 17:29:26 +00:00
static void handleDxInterrupt ( int in ) ; // fix #11047
static void ClearRFIDBuffer ( int ) ;
2021-01-16 14:39:33 +00:00
uint64_t rfid ;
2021-03-16 17:58:31 +00:00
uint32_t tagSize ;
const char * outFormat ;
uint64_t mqttRFIDKeypadBuffer ;
uint64_t webRFIDKeypadBuffer ;
2021-01-16 14:39:33 +00:00
static volatile uint64_t rfidBuffer ;
static volatile uint16_t bitCount ;
static volatile uint32_t lastFoundTime ;
2021-03-10 11:42:30 +00:00
// fix #11047
2022-02-14 17:29:26 +00:00
static volatile uint32_t bitTime ;
static volatile uint32_t FirstBitTimeStamp ;
2021-03-10 11:42:30 +00:00
static volatile uint32_t CodeGapTime ;
static volatile bool CodeComplete ;
static volatile RFID_store rfid_found [ ] ;
static volatile int currentFoundRFIDcount ;
2021-01-16 14:39:33 +00:00
} ;
Wiegand * oWiegand = new Wiegand ( ) ;
volatile uint64_t Wiegand : : rfidBuffer ;
volatile uint16_t Wiegand : : bitCount ;
volatile uint32_t Wiegand : : lastFoundTime ;
2022-02-14 17:29:26 +00:00
// fix for #11047
2021-03-10 11:42:30 +00:00
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 ;
2021-01-16 14:39:33 +00:00
2021-03-20 16:15:42 +00:00
2021-04-02 16:14:08 +01:00
void IRAM_ATTR Wiegand : : ClearRFIDBuffer ( int endIndex = WIEGAND_RFID_ARRAY_SIZE ) {
2021-03-20 16:15:42 +00:00
currentFoundRFIDcount = WIEGAND_RFID_ARRAY_SIZE - endIndex ; // clear all buffers
for ( int i = 0 ; i < endIndex ; i + + ) {
rfid_found [ i ] . RFID = 0 ;
rfid_found [ i ] . bitCount = 0 ;
}
}
2021-04-02 16:14:08 +01:00
void IRAM_ATTR Wiegand : : handleD1Interrupt ( ) { // Receive a 1 bit. (D0=high & D1=low)
2021-03-10 11:42:30 +00:00
handleDxInterrupt ( 1 ) ;
2021-01-16 14:39:33 +00:00
}
2021-04-02 16:14:08 +01:00
void IRAM_ATTR Wiegand : : handleD0Interrupt ( ) { // Receive a 0 bit. (D0=low & D1=high)
2022-02-14 17:29:26 +00:00
handleDxInterrupt ( 0 ) ;
2021-03-10 11:42:30 +00:00
}
2021-04-02 16:14:08 +01:00
void IRAM_ATTR Wiegand : : handleDxInterrupt ( int in ) {
2021-03-10 11:42:30 +00:00
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 ;
2022-02-14 17:29:26 +00:00
if ( ( diffTime > CodeGapTime ) & & ( bitCount > 0 ) ) {
2021-03-10 11:42:30 +00:00
// 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)
2021-03-20 16:15:42 +00:00
// unfortunately there's no timing defined for Wiegand. On my test reader the impulse time = 125 µs impulse gap time = 950 µs.
2021-03-10 11:42:30 +00:00
if ( currentFoundRFIDcount < WIEGAND_RFID_ARRAY_SIZE ) { // when reaching the end of rfid buffer we will overwrite the last one.
currentFoundRFIDcount + + ;
}
// start a new tag
2022-02-14 17:29:26 +00:00
rfidBuffer = 0 ;
2021-03-10 11:42:30 +00:00
bitCount = 0 ;
2022-02-14 17:29:26 +00:00
FirstBitTimeStamp = 0 ;
2021-03-10 11:42:30 +00:00
}
2022-02-14 17:29:26 +00:00
2021-03-10 11:42:30 +00:00
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
2021-03-20 16:15:42 +00:00
else if ( in = = 1 ) { rfidBuffer = ( rfidBuffer < < 1 ) | 1 ; } // Receive a 1 bit. (D0=high & D1=low): Leftshift + 1 bit
else { return ; } // (in==3) called by ScanForTag to get the last tag, because the interrupt handler is no longer called after receiving the last bit
2021-03-10 11:42:30 +00:00
2022-02-14 17:29:26 +00:00
bitCount + + ;
2021-03-10 11:42:30 +00:00
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.
}
2021-03-20 16:15:42 +00:00
else if ( bitCount = = 2 ) { // only calculate once per RFID tag, but restrict to values, which are in within a plausible range
bitTime = ( ( diffTime > ( WIEGAND_BIT_TIME_DEFAULT / 4 ) ) & & ( diffTime < ( 4 * WIEGAND_BIT_TIME_DEFAULT ) ) ) ? diffTime : WIEGAND_BIT_TIME_DEFAULT ;
2021-03-10 11:42:30 +00:00
CodeGapTime = WIEGAND_CODE_GAP_FACTOR * bitTime ;
2022-02-14 17:29:26 +00:00
}
2021-03-10 11:42:30 +00:00
//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
2021-01-16 14:39:33 +00:00
}
2021-03-20 16:15:42 +00:00
Wiegand : : Wiegand ( ) {
rfid = 0 ;
lastFoundTime = 0 ;
tagSize = 0 ;
rfidBuffer = 0 ;
bitCount = 0 ;
isInit = false ;
// fix #11047
bitTime = WIEGAND_BIT_TIME_DEFAULT ;
FirstBitTimeStamp = 0 ;
CodeGapTime = WIEGAND_CODE_GAP_FACTOR * bitTime ;
CodeComplete = false ;
ClearRFIDBuffer ( ) ;
outFormat = " u " ; // standard output format decimal
mqttRFIDKeypadBuffer = 0 ;
webRFIDKeypadBuffer = 0 ;
}
2021-01-16 14:39:33 +00:00
void Wiegand : : Init ( ) {
isInit = false ;
2021-01-21 15:12:44 +00:00
if ( PinUsed ( GPIO_WIEGAND_D0 ) & & PinUsed ( GPIO_WIEGAND_D1 ) ) { // Only start, if the Wiegang pins are selected
2021-01-17 16:50:58 +00:00
# if (DEV_WIEGAND_TEST_MODE)>0
2021-01-23 16:10:06 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " WIE: Init() " ) ) ;
2021-01-21 15:12:44 +00:00
# endif // DEV_WIEGAND_TEST_MODE>0
2021-01-16 14:39:33 +00:00
pinMode ( Pin ( GPIO_WIEGAND_D0 ) , INPUT_PULLUP ) ;
pinMode ( Pin ( GPIO_WIEGAND_D1 ) , INPUT_PULLUP ) ;
2021-03-16 17:58:31 +00:00
# if (DEV_WIEGAND_TEST_MODE==1) // overwrite the setting
pinMode ( Pin ( GPIO_WIEGAND_D0 ) , OUTPUT ) ;
pinMode ( Pin ( GPIO_WIEGAND_D1 ) , OUTPUT ) ;
# endif //(DEV_WIEGAND_TEST_MODE==1)
2021-01-17 16:50:58 +00:00
attachInterrupt ( Pin ( GPIO_WIEGAND_D0 ) , handleD0Interrupt , FALLING ) ;
attachInterrupt ( Pin ( GPIO_WIEGAND_D1 ) , handleD1Interrupt , FALLING ) ;
2021-01-21 15:12:44 +00:00
isInit = true ; // Helps to run only if correctly setup
2021-01-17 16:50:58 +00:00
# if (DEV_WIEGAND_TEST_MODE)>0
2021-01-23 16:10:06 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " WIE: Testmode, D0:%u, D1:%u " ) , Pin ( GPIO_WIEGAND_D0 ) , Pin ( GPIO_WIEGAND_D1 ) ) ; // For tests without reader attaiched
2021-01-17 16:50:58 +00:00
# else
2021-01-23 16:10:06 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " WIE: Wiegand Rfid Reader detected " ) ) ;
2021-01-21 15:12:44 +00:00
# endif // DEV_WIEGAND_TEST_MODE>0
2021-01-16 14:39:33 +00:00
}
2021-01-17 16:50:58 +00:00
# if (DEV_WIEGAND_TEST_MODE)>0
2021-01-16 14:39:33 +00:00
else {
2021-01-23 16:10:06 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " WIE: no GPIOs. " ) ) ;
2021-01-16 14:39:33 +00:00
}
2021-01-21 15:12:44 +00:00
# endif // DEV_WIEGAND_TEST_MODE>0
2021-01-16 14:39:33 +00:00
}
2021-03-10 11:42:30 +00:00
uint64_t Wiegand : : CheckAndConvertRfid ( uint64_t rfidIn , uint16_t bitCount ) {
2021-01-16 14:39:33 +00:00
uint8_t evenParityBit = 0 ;
2021-01-17 16:50:58 +00:00
uint8_t oddParityBit = ( uint8_t ) ( rfidIn & 0x1 ) ; // Last bit = odd parity
2021-01-16 14:39:33 +00:00
uint8_t calcParity = 0 ;
2021-03-10 11:42:30 +00:00
switch ( bitCount ) {
2021-01-16 14:39:33 +00:00
case 24 :
evenParityBit = ( rfidIn & 0x800000 ) ? 0x80 : 0 ;
rfidIn = ( rfidIn & 0x7FFFFE ) > > 1 ;
break ;
case 26 :
evenParityBit = ( rfidIn & 0x2000000 ) ? 0x80 : 0 ;
rfidIn = ( rfidIn & 0x1FFFFFE ) > > 1 ;
break ;
case 32 :
evenParityBit = ( rfidIn & 0x80000000 ) ? 0x80 : 0 ;
rfidIn = ( rfidIn & 0x7FFFFFFE ) > > 1 ;
break ;
case 34 :
2022-02-15 13:32:42 +00:00
evenParityBit = ( rfidIn & 0x200000000 ) ? 0x80 : 0 ;
rfidIn = ( rfidIn & 0x1FFFFFFFE ) > > 1 ;
2021-01-16 14:39:33 +00:00
break ;
default :
break ;
}
2021-01-21 15:12:44 +00:00
calcParity = CalculateParities ( rfidIn , bitCount ) ; // Check result on http://www.ccdesignworks.com/wiegand_calc.htm with raw tag as input
if ( calcParity ! = ( evenParityBit | oddParityBit ) ) { // Parity bit is wrong
2021-01-16 14:39:33 +00:00
rfidIn = 0 ;
2022-02-15 13:32:42 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " WIE: %_X parity error " ) , & rfidIn ) ;
2021-01-16 14:39:33 +00:00
}
2021-01-17 16:50:58 +00:00
# if (DEV_WIEGAND_TEST_MODE)>0
2021-01-23 16:10:06 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " WIE: even (left) parity: %u " ) , ( evenParityBit > > 7 ) ) ;
AddLog ( LOG_LEVEL_INFO , PSTR ( " WIE: even (calc) parity: %u " ) , ( calcParity & 0x80 ) > > 7 ) ;
AddLog ( LOG_LEVEL_INFO , PSTR ( " WIE: odd (right) parity: %u " ) , oddParityBit ) ;
AddLog ( LOG_LEVEL_INFO , PSTR ( " WIE: odd (calc) parity: %u " ) , ( calcParity & 0x01 ) ) ;
2021-01-21 15:12:44 +00:00
# endif // DEV_WIEGAND_TEST_MODE>0
2021-01-16 14:39:33 +00:00
return rfidIn ;
}
uint8_t Wiegand : : CalculateParities ( uint64_t tagWithoutParities , int tag_size = 26 ) {
2021-01-17 16:50:58 +00:00
// tag_size is the size of the final tag including the 2 parity bits
// So length if the tagWithoutParities should be (tag_size-2) !! That will be not profed and
// lead to wrong results if the input value is larger!
// Calculated start parity (even) will be returned as bit 8
// calculated end parity (odd) will be returned as bit 1
2021-01-16 14:39:33 +00:00
uint8_t retValue = 0 ;
tag_size - = 2 ;
2021-01-17 16:50:58 +00:00
if ( tag_size < = 0 ) { return retValue ; } // Prohibit div zero exception and other wrong inputs
uint8_t parity = 1 ; // Check for odd parity on LSB
for ( uint8_t i = 0 ; i < ( tag_size / 2 ) ; i + + ) {
parity ^ = ( tagWithoutParities & 1 ) ;
tagWithoutParities > > = 1 ;
}
2021-01-16 14:39:33 +00:00
retValue | = parity ;
2021-01-17 16:50:58 +00:00
parity = 0 ; // Check for even parity on MSB
2021-01-16 14:39:33 +00:00
while ( tagWithoutParities ) {
2021-01-17 16:50:58 +00:00
parity ^ = ( tagWithoutParities & 1 ) ;
tagWithoutParities > > = 1 ;
}
retValue | = ( parity < < 7 ) ;
2021-01-16 14:39:33 +00:00
return retValue ;
}
2021-03-10 11:42:30 +00:00
bool Wiegand : : WiegandConversion ( uint64_t rfidBuffer , uint16_t bitCount ) {
2021-01-16 14:39:33 +00:00
bool bRet = false ;
2021-01-17 16:50:58 +00:00
# if (DEV_WIEGAND_TEST_MODE)>0
2021-01-23 16:10:06 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " WIE: Raw tag %llu, Bit count %u " ) , rfidBuffer , bitCount ) ;
2021-01-21 15:12:44 +00:00
# endif // DEV_WIEGAND_TEST_MODE>0
2021-01-21 12:44:39 +00:00
if ( ( 24 = = bitCount ) | | ( 26 = = bitCount ) | | ( 32 = = bitCount ) | | ( 34 = = bitCount ) ) {
// 24, 26, 32, 34-bit Wiegand codes
2021-01-21 15:12:44 +00:00
rfid = CheckAndConvertRfid ( rfidBuffer , bitCount ) ;
2021-01-21 12:44:39 +00:00
tagSize = bitCount ;
bRet = true ;
}
else if ( 4 = = bitCount ) {
// 4-bit Wiegand codes for keypads
2021-03-16 17:58:31 +00:00
rfid = ( int ) ( rfidBuffer & 0x0000000F ) ;
2021-01-21 12:44:39 +00:00
tagSize = bitCount ;
bRet = true ;
}
else if ( 8 = = bitCount ) {
// 8-bit Wiegand codes for keypads with integrity
// 8-bit Wiegand keyboard data, high nibble is the "NOT" of low nibble
// eg if key 1 pressed, data=E1 in binary 11100001 , high nibble=1110 , low nibble = 0001
char highNibble = ( rfidBuffer & 0xf0 ) > > 4 ;
char lowNibble = ( rfidBuffer & 0x0f ) ;
if ( lowNibble = = ( ~ highNibble & 0x0f ) ) { // Check if low nibble matches the "NOT" of high nibble.
2021-03-16 17:58:31 +00:00
rfid = ( int ) ( lowNibble ) ;
2021-01-21 12:44:39 +00:00
bRet = true ;
} else {
bRet = false ;
2021-01-16 14:39:33 +00:00
}
2021-01-21 12:44:39 +00:00
tagSize = bitCount ;
2021-01-17 16:50:58 +00:00
} else {
// Time reached but unknown bitCount, clear and start again
2021-03-16 17:58:31 +00:00
tagSize = 0 ;
2021-01-17 16:50:58 +00:00
bRet = false ;
2021-01-16 14:39:33 +00:00
}
2021-01-17 16:50:58 +00:00
# if (DEV_WIEGAND_TEST_MODE)>0
2021-01-23 16:10:06 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " WIE: Tag out %llu, tag size %u " ) , rfid , tagSize ) ;
2021-01-21 15:12:44 +00:00
# endif // DEV_WIEGAND_TEST_MODE>0
2021-01-17 10:39:21 +00:00
return bRet ;
2021-01-16 14:39:33 +00:00
}
2021-03-11 11:33:35 +00:00
void Wiegand : : setOutputFormat ( void )
{
2021-03-16 17:58:31 +00:00
if ( GetOption ( WIEGAND_OPTION_HEX ) = = 0 ) { outFormat = " u " ; }
else { outFormat = " X " WIEGAND_OPTION_HEX_POSTFIX ; }
}
void Wiegand : : HandleKeyPad ( void ) { // will be called if a valid key pad input was recognized
if ( GetOption ( WIEGAND_OPTION_KEYPAD_TO_TAG ) = = 0 ) { // handle all key pad inputs as ONE Tag until # is recognized
if ( ( tagSize = = 4 ) | | ( tagSize = = 8 ) ) {
//only handle Keypad strokes if it is requested
if ( rfid > = 0x0a ) { // # * as end of input detected -> all key values which are larger than 9
rfid = mqttRFIDKeypadBuffer ; // original tagsize of 4 or 8 will be kept.
webRFIDKeypadBuffer = 0 ; // can be resetted, because now rfid > 0 will be used at web interface
2022-02-14 17:29:26 +00:00
mqttRFIDKeypadBuffer = 0 ;
2021-03-16 17:58:31 +00:00
}
else {
mqttRFIDKeypadBuffer = ( mqttRFIDKeypadBuffer * 10 ) + rfid ; //left shift + new key
webRFIDKeypadBuffer = mqttRFIDKeypadBuffer ; // visualising the current typed keys
rfid = 0 ;
2022-02-14 17:29:26 +00:00
tagSize = 0 ;
2021-03-16 17:58:31 +00:00
}
}
else { //it's not a key pad entry, so another key come in, we will reset the buffer, if it is not finished yet
2022-02-14 17:29:26 +00:00
webRFIDKeypadBuffer = 0 ;
mqttRFIDKeypadBuffer = 0 ;
2021-03-16 17:58:31 +00:00
}
}
2021-03-11 11:33:35 +00:00
}
2021-01-16 14:39:33 +00:00
void Wiegand : : ScanForTag ( ) {
2021-03-10 11:42:30 +00:00
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
2021-03-11 11:33:35 +00:00
// format MQTT output
2022-02-14 17:29:26 +00:00
// setOutputFormat();
// char sFormat[50];
// snprintf( sFormat, 50, PSTR(",\"Wiegand\":{\"UID\":%%0ll%s,\"" D_JSON_SIZE "\":%%%s}}"), outFormat, outFormat);
2021-03-10 11:42:30 +00:00
for ( int i = 0 ; i < WIEGAND_RFID_ARRAY_SIZE ; i + + )
{
2021-08-24 14:54:02 +01:00
if ( rfid_found [ i ] . RFID ! = 0 | | ( rfid_found [ i ] . RFID = = 0 & & i = = 0 ) ) {
2021-03-10 11:42:30 +00:00
uint64_t oldTag = rfid ;
bool validKey = WiegandConversion ( rfid_found [ i ] . RFID , rfid_found [ i ] . bitCount ) ;
# if (DEV_WIEGAND_TEST_MODE)>0
2021-03-20 16:15:42 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " WIE: ValidKey: %d Previous tag %llu " ) , validKey , oldTag ) ;
2021-03-10 11:42:30 +00:00
# endif // DEV_WIEGAND_TEST_MODE>0
if ( validKey ) { // Only in case of valid key do action. Issue#10585
2022-02-14 17:29:26 +00:00
HandleKeyPad ( ) ; //support one tag for multi key input
2021-03-16 17:58:31 +00:00
if ( tagSize > 0 ) { //do output only for rfids which are complete
if ( oldTag = = rfid ) {
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " WIE: Old tag " ) ) ;
}
2022-02-14 17:29:26 +00:00
// ResponseTime_P(sFormat, rfid, tagSize);
// Tasmota does not support 64-bit decimal output specifier (%llu) saving 60k code
if ( GetOption ( WIEGAND_OPTION_HEX ) = = 0 ) {
ResponseTime_P ( PSTR ( " , \" Wiegand \" :{ \" UID \" :%lu, \" " D_JSON_SIZE " \" :%d}} " ) , ( uint32_t ) rfid , tagSize ) ;
} else {
2022-02-15 13:32:42 +00:00
ResponseTime_P ( PSTR ( " , \" Wiegand \" :{ \" UID \" : \" %1_X " WIEGAND_OPTION_HEX_POSTFIX " \" , \" " D_JSON_SIZE " \" :%d}} " ) , & rfid , tagSize ) ;
2022-02-14 17:29:26 +00:00
}
2021-03-16 17:58:31 +00:00
MqttPublishTeleSensor ( ) ;
2021-03-10 11:42:30 +00:00
}
}
2021-01-21 15:12:44 +00:00
}
2021-01-17 16:50:58 +00:00
}
2021-03-10 11:42:30 +00:00
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
2022-02-14 17:29:26 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " WIE: ScanForTag() %lu tags added while working on buffer " ) , ( currentFoundRFIDcount - lastFoundRFIDcount ) ) ;
2021-03-10 11:42:30 +00:00
}
2022-02-14 17:29:26 +00:00
ClearRFIDBuffer ( ) ; //reset array
2021-03-10 11:42:30 +00:00
# if (DEV_WIEGAND_TEST_MODE)>0
2022-02-14 17:29:26 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " WIE: ScanForTag() time elapsed %lu " ) , ( micros ( ) - startTime ) ) ;
2021-03-10 11:42:30 +00:00
# endif
2021-01-17 16:50:58 +00:00
}
2022-02-14 17:29:26 +00:00
2021-01-16 14:39:33 +00:00
}
# ifdef USE_WEBSERVER
void Wiegand : : Show ( void ) {
2022-02-14 17:29:26 +00:00
// setOutputFormat();
// char sFormat [30];
// snprintf( sFormat, 30,PSTR("{s}Wiegand UID{m}%%ll%s {e}"), outFormat);
// if (tagSize>0) { WSContentSend_PD(sFormat, rfid); }
// else { WSContentSend_PD(sFormat, webRFIDKeypadBuffer); }
// Tasmota does not support 64-bit decimal output specifier (%llu) saving 60k code
if ( GetOption ( WIEGAND_OPTION_HEX ) = = 0 ) {
WSContentSend_P ( PSTR ( " {s}Wiegand UID{m}%lu{e} " ) , ( tagSize > 0 ) ? ( uint32_t ) rfid : ( uint32_t ) webRFIDKeypadBuffer ) ;
} else {
2022-02-15 13:32:42 +00:00
WSContentSend_P ( PSTR ( " {s}Wiegand UID{m}%1_X " WIEGAND_OPTION_HEX_POSTFIX " {e} " ) , ( tagSize > 0 ) ? & rfid : & webRFIDKeypadBuffer ) ;
2022-02-14 17:29:26 +00:00
}
2021-03-16 17:58:31 +00:00
# if (DEV_WIEGAND_TEST_MODE)>0
AddLog ( LOG_LEVEL_INFO , PSTR ( " WIE: Tag %llu, Bits %u " ) , rfid , bitCount ) ;
# endif // DEV_WIEGAND_TEST_MODE>0
2021-01-16 14:39:33 +00:00
}
# endif // USE_WEBSERVER
2021-03-16 17:58:31 +00:00
# if (DEV_WIEGAND_TEST_MODE==1)
void CmndTag ( void ) ;
void CmndTagSize ( void ) ;
void CmndTagGap ( void ) ;
void CmndTimeReset ( void ) ;
void CmndAllReset ( void ) ;
void CmndSend ( void ) ;
void CmndBitTime ( void ) ;
void CmndInterBitTime ( void ) ;
unsigned int setTagSize ( char * ) ;
unsigned int setTag ( char * ) ;
void sendBit ( unsigned int b ) ;
void sendTag ( uint32_t Tag , uint32_t TagSize ) ;
uint32_t currTag = 0 ;
uint32_t currTagSize = 26 ; //default value 26 Wiegand
uint32_t currBitTime = ( WIEGAND_BIT_TIME_DEFAULT / 10 ) ; //length of the bit impluse in µs
uint32_t currInterBitTime = ( ( WIEGAND_BIT_TIME_DEFAULT / 10 ) * 9 ) ; //time to wait before next bit is send in µs
uint32_t currTagGabTime = ( WIEGAND_BIT_TIME_DEFAULT * WIEGAND_CODE_GAP_FACTOR ) ; //time to wait before next tag is send in µs
2022-02-14 17:29:26 +00:00
2021-03-16 17:58:31 +00:00
void CmndTag ( void ) {
if ( XdrvMailbox . data_len > 0 ) {
2022-02-14 17:29:26 +00:00
currTag = strtoul ( XdrvMailbox . data , nullptr , 0 ) ;
2021-03-16 17:58:31 +00:00
}
ResponseCmndNumber ( currTag ) ;
}
void CmndTagSize ( void ) {
if ( XdrvMailbox . data_len > 0 ) {
currTagSize = setTagSize ( XdrvMailbox . data ) ;
}
ResponseCmndNumber ( currTagSize ) ;
}
void CmndTagGap ( void ) {
if ( XdrvMailbox . data_len > 0 ) {
currTagGabTime = strtoul ( XdrvMailbox . data , nullptr , 0 ) ;
if ( currTagGabTime < ( currBitTime + currInterBitTime ) ) // doesn't make sense
{ currTagGabTime = ( currBitTime + currInterBitTime ) * WIEGAND_CODE_GAP_FACTOR ; }
}
ResponseCmndNumber ( currTagGabTime ) ;
}
void CmndBitTime ( void ) {
if ( XdrvMailbox . data_len > 0 ) {
uint32_t newBitTime = strtoul ( XdrvMailbox . data , nullptr , 0 ) ;
if ( ( newBitTime > = 100 ) & & ( newBitTime < = 500000 ) ) // accept only values between 100µs and 5s
{ currBitTime = newBitTime ; }
}
ResponseCmndNumber ( currBitTime ) ;
}
void CmndInterBitTime ( void ) {
if ( XdrvMailbox . data_len > 0 ) {
uint32_t newInterBitTime = strtoul ( XdrvMailbox . data , nullptr , 0 ) ;
if ( ( newInterBitTime > = currBitTime ) & & ( newInterBitTime < = ( 100 * currBitTime ) ) ) // accept only values between 100µs and 5s
{ currInterBitTime = newInterBitTime ; }
}
ResponseCmndNumber ( currInterBitTime ) ;
}
void CmndTimeReset ( void ) {
2022-02-14 17:29:26 +00:00
currBitTime = ( WIEGAND_BIT_TIME_DEFAULT / 10 ) ;
currInterBitTime = ( ( WIEGAND_BIT_TIME_DEFAULT / 10 ) * 9 ) ;
currTagGabTime = ( WIEGAND_BIT_TIME_DEFAULT * WIEGAND_CODE_GAP_FACTOR ) ;
2021-03-16 17:58:31 +00:00
ResponseCmndChar_P ( PSTR ( " All timings reset to default! " ) ) ;
}
void CmndAllReset ( void ) {
CmndTimeReset ( ) ;
currTagSize = 26 ;
ResponseCmndChar_P ( PSTR ( " All timings and tag size reset to default " ) ) ;
}
void CmndSend ( void ) {
if ( XdrvMailbox . data_len > 0 ) { // parameter [tag[:tagsize];tag[:tagsize];...]
char * parameter = strtok ( XdrvMailbox . data , " ; " ) ;
while ( parameter ! = nullptr ) {
char * pTagSize = strchr ( parameter , ' : ' ) ; // find optional tagsizes
if ( pTagSize ! = 0 ) { // 2 parameters found tag:tagsize
* pTagSize = 0 ; //replace separator ':' by \0 string end
currTag = setTag ( parameter ) ; // is now ending before tagsize
2022-02-14 17:29:26 +00:00
pTagSize + + ; //set the starting char of tagsize correctly
2021-03-16 17:58:31 +00:00
currTagSize = setTagSize ( pTagSize ) ;
ResponseCmndChar ( pTagSize ) ;
}
else { //only one parameter (tag) found
2022-02-14 17:29:26 +00:00
currTag = setTag ( parameter ) ;
2021-03-16 17:58:31 +00:00
}
ResponseCmndChar ( parameter ) ;
sendTag ( currTag , currTagSize ) ;
ResponseCmndNumber ( currTag ) ;
parameter = strtok ( nullptr , " ; " ) ;
}
}
else { // send last used values again
sendTag ( currTag , currTagSize ) ;
ResponseCmndNumber ( currTag ) ;
}
}
unsigned int setTag ( char * newTag ) {
unsigned int retValue = strtoul ( newTag , nullptr , 0 ) ;
if ( ( currTagSize = = 4 ) | | ( currTagSize = = 8 ) ) //key pad input simulation requested
{ retValue & = 0x0F ; }
return retValue ;
}
unsigned int setTagSize ( char * newTagSize ) {
unsigned int retValue = strtoul ( newTagSize , nullptr , 0 ) ;
// accept only supported TagSize
if ( retValue < = 4 ) { retValue = 4 ; }
else if ( retValue < = 8 ) { retValue = 8 ; }
else if ( retValue < = 24 ) { retValue = 24 ; }
else if ( retValue < = 26 ) { retValue = 26 ; }
else if ( retValue < = 32 ) { retValue = 32 ; }
else if ( retValue < = 34 ) { retValue = 34 ; }
else { retValue = 26 ; } //default value
return retValue ;
}
void sendBit ( unsigned int b ) {
int sel = ( b = = 0 ) ? Pin ( GPIO_WIEGAND_D0 ) : Pin ( GPIO_WIEGAND_D1 ) ;
digitalWrite ( sel , 0 ) ;
delayMicroseconds ( currBitTime ) ; // bit impuls time
digitalWrite ( sel , 1 ) ;
delayMicroseconds ( currBitTime + currInterBitTime ) ; // bit + inter bit gap time
}
void sendPlainTag ( uint32_t pTag , uint32_t pTagSize ) { // send tag without parity
for ( int i = 1 ; i < = pTagSize ; + + i )
{
sendBit ( ( pTag > > ( pTagSize - i ) ) & 1 ) ;
}
}
void sendTag ( uint32_t Tag , uint32_t TagSize ) {
// TagSize is the requested output tagSize. means b.e. 24 bit == 24 Tag without parity 26 bit = 24 bit with parity bits
// supported tag sizes 4/8 for key pad simulation 24/26 and 32/34 for RFID tags
switch ( TagSize ) {
case 24 :
case 32 :
case 4 :
sendPlainTag ( Tag , TagSize ) ;
break ;
case 26 :
case 34 :
uint8_t parity ;
parity = oWiegand - > CalculateParities ( Tag , TagSize ) ;
sendBit ( parity & 0x01 ) ; //even parity (starting parity)
sendPlainTag ( Tag , TagSize - 2 ) ;
sendBit ( parity & 0x80 ) ; //odd parity (ending parity)
break ;
case 8 : // high nibble is ~ low nibble
Tag = Tag & 0x0F ; // low nibble in case of more the one char input it will be cut here
Tag = Tag | ( ( ~ Tag ) < < 4 ) ;
2022-02-14 17:29:26 +00:00
sendPlainTag ( Tag , TagSize ) ;
2021-03-16 17:58:31 +00:00
break ;
}
//delay to simulate end of tag
delayMicroseconds ( currTagGabTime ) ; // inter code gap
return ;
}
const char kWiegandCommands [ ] PROGMEM = " Wie| " // No prefix
" Tag| "
" TagSize| "
" TagGap| "
" BitTime| "
" InterBitTime| "
" TimeReset| "
" AllReset| "
" Send " ;
void ( * const WiegandCommand [ ] ) ( void ) PROGMEM = {
& CmndTag ,
& CmndTagSize ,
& CmndTagGap ,
& CmndBitTime ,
& CmndInterBitTime ,
& CmndTimeReset ,
& CmndAllReset ,
& CmndSend
} ;
# endif //(DEV_WIEGAND_TEST_MODE==1)
2021-01-16 14:39:33 +00:00
/*********************************************************************************************\
* Interface
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
bool Xsns82 ( byte function ) {
bool result = false ;
2021-01-21 15:12:44 +00:00
if ( FUNC_INIT = = function ) {
oWiegand - > Init ( ) ;
}
else if ( oWiegand - > isInit ) {
switch ( function ) {
2021-03-10 11:42:30 +00:00
case FUNC_EVERY_100_MSECOND : // fix for #11047 Wiegand 26/34 missed some key press
oWiegand - > ScanForTag ( ) ;
2021-01-21 15:12:44 +00:00
break ;
2021-01-17 16:50:58 +00:00
# ifdef USE_WEBSERVER
2021-01-21 15:12:44 +00:00
case FUNC_WEB_SENSOR :
oWiegand - > Show ( ) ;
break ;
2021-01-17 16:50:58 +00:00
# endif // USE_WEBSERVER
2021-03-16 17:58:31 +00:00
# if (DEV_WIEGAND_TEST_MODE==1)
case FUNC_COMMAND :
result = DecodeCommand ( kWiegandCommands , WiegandCommand ) ;
break ;
# endif //(DEV_WIEGAND_TEST_MODE==1)
2021-01-21 15:12:44 +00:00
}
2021-01-16 14:39:33 +00:00
}
return result ;
}
2021-01-17 16:50:58 +00:00
2021-01-16 14:39:33 +00:00
# endif // USE_WIEGAND