#include "esphome.h"
/**
* Wiegand Reader Custom Device
*
* Adapted from Greg Doerr (https://github.com/gdoerr)
* Sourced from https://github.com/monkeyboard/Wiegand-Protocol-Library-for-Ardu
ino
*
*/
class WiegandReader : public PollingComponent, public Sensor {
public:
const char* TAG = "Wiegand";
WiegandReader(int pinD0, int pinD1) : PollingComponent(200), pinD0(pinD0), p
inD1(pinD1) {
}
/**
* Initial setup
*/
void setup() override {
_lastWiegand = 0;
_cardTempHigh = 0;
_cardTemp = 0;
_code = 0;
_wiegandType = 0;
_bitCount = 0;
_holdBitCount = 0;
// Configure the input pins
//
pinMode(pinD0, INPUT);
pinMode(pinD1, INPUT);
// Attach the interrupts
// Wiegand protocol uses FALLING
//
attachInterrupt(digitalPinToInterrupt(pinD0), ReadD0, FALLING); // Hard
ware interrupt - high to low pulse
attachInterrupt(digitalPinToInterrupt(pinD1), ReadD1, FALLING); // Hard
ware interrupt - high to low pulse
}
void update() override {
//
// See if we have a valid code
//
noInterrupts();
bool rc = DoWiegandConversion();
interrupts();
if (rc)
{
ESP_LOGV(TAG, "_holdBitCount = %i", _holdBitCount);
ESP_LOGD(TAG, "resultCode = %s", rc ? "true" : "false" );
ESP_LOGD(TAG, "Tag scanned = %i", _code);
ESP_LOGV(TAG, "_cardTemp = %i", _cardTemp);
ESP_LOGV(TAG, "_cardTempHigh = %i", _cardTempHigh);
ESP_LOGV(TAG, "_wiegandType = %i", _wiegandType);
ESP_LOGV(TAG, "_bitCount = %i", _bitCount);
publish_state(_code);
char buf[16];
sprintf(buf, "%0lX", _code);
char hex[16];
sprintf(hex, "%02lX:%02lX:%02lX:%02lX", (_code >> 24), (_code >> 16
& 0xff), (_code >> 8 & 0xff), (_code & 0xff));
ESP_LOGD(TAG, "Tag ID = %s %s", buf, hex);
}
}
private:
static volatile unsigned long _cardTempHigh;
static volatile unsigned long _cardTemp;
static volatile unsigned long _lastWiegand;
static volatile int _bitCount;
static volatile int _holdBitCount;
static int _wiegandType;
static unsigned long _code;
unsigned long lastCode = 0;
std::string keyCodes = "";
int pinD0;
int pinD1;
std::string serviceName;
/**
* D0 Interrupt Handler
*/
static void ICACHE_RAM_ATTR ReadD0()
{
_bitCount++; // Increment bit
count for Interrupt connected to D0
if (_bitCount > 31) // If bit count
more than 31, process high bits
{ // shift value t
o high bits
_cardTempHigh |= ((0x80000000 & _cardTemp)>>31);
_cardTempHigh <<= 1;
_cardTemp <<=1;
}
else
{
_cardTemp <<= 1; // D0 represent binary 0, so just left s
hift card data
}
_lastWiegand = millis(); // Save the time the last wiegand bit wa
s received
}
/**
* D1 Interrupt Handler
*/
static void ICACHE_RAM_ATTR ReadD1()
{
_bitCount ++; // Increment bit
count for Interrupt connected to D1
if (_bitCount > 31) // If bit count
more than 31, process high bits
{
_cardTempHigh |= ((0x80000000 & _cardTemp)>>31); // shift value t
o high bits
_cardTempHigh <<= 1;
_cardTemp |= 1;
_cardTemp <<=1;
}
else
{
_cardTemp |= 1; // D1 represent binary 1, so OR card dat
a with 1 then
_cardTemp <<= 1; // left shift card data
}
_lastWiegand = millis(); // Save the time the last wiegand bit wa
s received
}
/**
* Extract the Card ID from the received bit stream
* @param codehigh
* @param codelow
* @param bitlength
* @return
*/
unsigned long getCardId(volatile unsigned long *codehigh, volatile unsigned
long *codelow, char bitlength)
{
if (bitlength==24)
return (*codelow & 0x7FFFFFE) >>1;
if (bitlength==26) // EM tag
return (*codelow & 0x1FFFFFE) >>1;
if (bitlength==32)
return (*codelow & 0x7FFFFFE) >>1;
if (bitlength==34) // Mifare
{
*codehigh = *codehigh & 0x03; // only need the 2 LSB o
f the codehigh
*codehigh <<= 30; // shift 2 LSB to MSB
*codelow >>=1;
return *codehigh | *codelow;
}
return *codelow; // EM tag or Mifare with
out parity bits
}
/**
* Convert the received bitstream
* @return
*/
bool DoWiegandConversion () {
unsigned long cardID;
unsigned long sysTick = millis();
_holdBitCount = _bitCount;
if ((sysTick - _lastWiegand) > 25)
// if no more signal coming through after 25ms
{
if ((_bitCount==24) || (_bitCount==26) || // Wiegand 26 = 24 or 2
6
(_bitCount==32) || (_bitCount==34) || // Wiegand 34 = 32 or 3
4
(_bitCount==8) || (_bitCount==4)) // keypress = 4 or 8
{
_cardTemp >>= 1; // shift right 1 bit to
get back the real value - interrupt done 1 left shift in advance
if (_bitCount>32) // bit count more than 3
2 bits, shift high bits right to make adjustment
_cardTempHigh >>= 1;
if (_bitCount==8) { // keypress wiegand with
integrity
// 8-bit Wiegand keyboar
d 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 = (_cardTemp & 0xf0) >>4;
char lowNibble = (_cardTemp & 0x0f);
_wiegandType=_bitCount;
_bitCount=0;
_cardTemp=0;
_cardTempHigh=0;
if (lowNibble == (~highNibble & 0x0f)) // check if low
nibble matches the "NOT" of high nibble.
{
_code = (int)lowNibble;
return true;
}
else
{
_lastWiegand=sysTick;
_bitCount=0;
_cardTemp=0;
_cardTempHigh=0;
return false;
}
//
// TODO: Handle validation failure case!
//
} else if (4 == _bitCount) {
//
// 4-bit Wiegand codes have no data integrity check so we ju
st
// read the LOW nibble.
//
_code = (int)(_cardTemp & 0x0000000F);
_wiegandType = _bitCount;
_bitCount = 0;
_cardTemp = 0;
_cardTempHigh = 0;
return true;
}
else // wiegand 26 or wiegand 34
{
cardID = getCardId (&_cardTempHigh, &_cardTemp, _bitCount);
_wiegandType=_bitCount;
_bitCount=0;
_cardTemp=0;
_cardTempHigh=0;
_code=cardID;
return true;
}
}
else
{
// well time over 25 ms and bitCount !=8 , !=26, !=34,
// must be noise or nothing then.
//
_lastWiegand=sysTick;
_bitCount=0;
_cardTemp=0;
_cardTempHigh=0;
return false;
}
}
else
return false;
}
};
volatile unsigned long WiegandReader::_cardTempHigh = 0;
volatile unsigned long WiegandReader::_cardTemp = 0;
volatile unsigned long WiegandReader::_lastWiegand = 0;
volatile int WiegandReader::_bitCount = 0;
volatile int WiegandReader::_holdBitCount = 0;
unsigned long WiegandReader::_code = 0;
int WiegandReader::_wiegandType = 0;
YAML Sensor definition code:
sensor:
platform: custom
lambda: |-
auto wiegand = new WiegandReader(D6, D7);
App.register_component(wiegand);
return {wiegand};
sensors:
name: "Card ID"
on_value:
then:
- logger.log: "Triggered"
- homeassistant.tag_scanned: !lambda 'return to_string(x);'