esphome / feature-requests

ESPHome Feature Request Tracker
https://esphome.io/
411 stars 26 forks source link

Wiegand interface #211

Closed golfvert closed 1 year ago

golfvert commented 5 years ago

Describe the problem you have/What new integration you would like I am using wiegand access. It would be nice to be able to integrate this kind of equipment to ESPHome

Please describe your use case for this integration and alternatives you've tried: At the moment, I am using a home made script. Having one solution for all would be nice.

Additional context

OttoWinter commented 5 years ago

I think the RDM6300 uses wiegand - could you try with the rdm6300 integration?

Zebble commented 5 years ago

The RDM6300 seems to use a standard UART protocol. Wiegand is its own beast. It would require some interrupt-driven communication for accuracy, but does seem fairly simple.

There's a good library that can figure out the number of bits in the data automagically. If I had time, I'd port it. Any takers?

https://github.com/paulo-raca/YetAnotherArduinoWiegandLibrary

nerdosity commented 5 years ago

That would be very good. I have a weigand reader outside, knowing the tag read would be perfect for integrating it into Home Assistant. Not knowing the tag I have mapped the relay as an ON/OFF, and I will have to put another rfid reader integrated for automations since I want some automation just for my tag and my girlfriend's, not for other tags.

beikeland commented 5 years ago

I have a couple of Siemens AR6182-MX readers (w/keypads) that can be configure to use a Wiegand interface, would be interesting to get this hooked up to EspHome.

Tried the YetAnotherArduinoWiegandLibrary and it works on arduino but not ESP8266. Found https://github.com/esprfid/esp-rfid which uses https://github.com/monkeyboard/Wiegand-Protocol-Library-for-Arduino.git but this didn't boot on my NodeMCU board at all. Might poke more later, but in case the links are of interest ☝️

beikeland commented 5 years ago

This fixes the library used in esp-rfid as linked to above https://github.com/monkeyboard/Wiegand-Protocol-Library-for-Arduino/pull/38

Unfortunately I need other bitcounts than standard depending on card type and optional pin length so I'll probably do a AR6182-MX library instead of a generic Wiegand.

nerdosity commented 5 years ago

@beikeland I am using esp-rfid project on my nodeMCU since a couple of weeks, just use version 1.0.2 since latest one has problems with MQTT messages for tags.

beikeland commented 5 years ago

My primary motivation for looking into using ESPhome was to not rely on MQTT. I've got the library working and sorted out separating Pin, Card and Card+Pin in an Arduino sketch running on a NodeMCU-devkit. So just have to wrap my head around the abstraction needed to write a new component for the Wiegand bus and a sensor or something on top of that, or find that someone beats me to it while trying to understand the ESPhome code:)

nerdosity commented 5 years ago

I understand that, I just replied to the fact that it wasn't booting on your nodeMCU ;)

beikeland commented 5 years ago

That was due to the missing ICACHE attribute for the interrupt handler in the underlying library. Fixed it.

On Thu, Jul 18, 2019 at 1:34 PM nerdosity notifications@github.com wrote:

I understand that, I just replied to the fact that it wasn't booting on your nodeMCU ;)

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/esphome/feature-requests/issues/211?email_source=notifications&email_token=AAUTS3RPMROPNEK6IKZNRDLQABINZA5CNFSM4HKTC32KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD2IGHOQ#issuecomment-512779194, or mute the thread https://github.com/notifications/unsubscribe-auth/AAUTS3URDCHZMNLVY22GOADQABINZANCNFSM4HKTC32A .

beikeland commented 5 years ago

Still trying to work out how to add the components to ESPHome, maybe one day. Sharing a gist with the working arduino sketch for esp8266 and AR6182 reader. Reader uses an electric wiegand interface, but custom protocol. https://gist.github.com/beikeland/145b20b1fc585049ea8951eddfc5732e

CountParadox commented 4 years ago

I have a bunch of HID Prox readers, would love a component to use them with ESPHome! Currently using them with https://github.com/TomHarkness/homeID

pricard32 commented 4 years ago

Any news on that? I've bought a keypad that use this protocol and would like to wiring it to an ESP32 running ESPHOME

edwardscaleb9 commented 4 years ago

Also interested in using wiegand keypads connected to an ESP8266/ESP32.

pricard32 commented 4 years ago

Can you help me building the plugin ?

edwardscaleb9 commented 4 years ago

I’d be happy to give it a go, but I’m not much of a programmer these days. More of a scripter I’d say.

While I’m thinking about it, one feature I’d like to see is the ability to set codes/card numbers in HomeAssistant (or whatever frontend) and have them cached locally on the ESP.

CountParadox commented 4 years ago

You must need the card number to be passed to home assistant, then you can do what you want with it.

On Sun, 12 Jan 2020, 12:49 am Caleb Edwards, notifications@github.com wrote:

I’d be happy to give it a go, but I’m not much of a programmer these days. More of a scripter I’d say.

While I’m thinking about it, one feature I’d like to see is the ability to set codes/card numbers in HomeAssistant (or whatever frontend) and have them cached locally on the ESP.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/esphome/feature-requests/issues/211?email_source=notifications&email_token=ABP5BVHK2LB73ZEJCCAAB2DQ5HE53A5CNFSM4HKTC32KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEIWCK5A#issuecomment-573318516, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABP5BVCRMOCRY5SU5SE65U3Q5HE53ANCNFSM4HKTC32A .

Zebble commented 4 years ago

If you want local cache / standalone control, you might want to look at the esprfid project. It can also talk to Mqtt so can also be manually integrated with stuff like Hass.

https://github.com/esprfid/esp-rfid

You don't need their special board, any esp8266 will do. I used it to handle card reading/users and had Hass control the lock. Worked quite well.

Still would be good to have weigand in esphome, to keep everything on one platform.

Sent from Ninehttp://www.9folders.com/


From: Caleb Edwards notifications@github.com Sent: Saturday, January 11, 2020 8:49 AM To: esphome/feature-requests Cc: Wade J. Weppler; Comment Subject: Re: [esphome/feature-requests] Wiegand interface (#211)

I’d be happy to give it a go, but I’m not much of a programmer these days. More of a scripter I’d say.

While I’m thinking about it, one feature I’d like to see is the ability to set codes/card numbers in HomeAssistant (or whatever frontend) and have them cached locally on the ESP.

— You are receiving this because you commented. Reply to this email directly, view it on GitHubhttps://github.com/esphome/feature-requests/issues/211?email_source=notifications&email_token=AAGOH54GIWWMK5GXAEOSI53Q5HE53A5CNFSM4HKTC32KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEIWCK5A#issuecomment-573318516, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AAGOH5Y4SLFAYOZPNN365CDQ5HE53ANCNFSM4HKTC32A.

pricard32 commented 4 years ago

I'm not interest of just RFID. I would like the complete wiegand protocol. Either Keypad of RDIF will be pass to HA.

And like @edwardscaleb9 said, it would be great to program the keypas via HA.

I've found online a library for Weigand, but dont have the skills to implement it with ESPHOME.

Someone with those skill would want to help us ?

gdoerr commented 4 years ago

I was able to implement a CustomAPIDevice using code from this Github repo without any issues. The code works as is (I had to drop the IRAM_ATTR on the interrupt handlers in order to get it to link but it's working well for me.

I can post the sample custom device code if anyone is interested.

pricard32 commented 4 years ago

@gdoerr i’m really interested to see how you did it !!

gdoerr commented 4 years ago

Save the code below as wiegand_device.h and include it as a custom component in your YAML file.

custom_component:
  - lambda: |-
       auto wiegand = new WiegandReader(pin0, pin1, "script.to_call");
       return {wiegand};

pin0 should be the GPIO connected to the D0 signal and pin1 is the GPIO connected to the D1 signal. Replace "script.to_call" with the name of the script or service to call. The code entered will be passed as the parameter code.

#include "esphome.h"

/**
 * Wiegand Reader Custom Device
 *
 * Copied from https://github.com/monkeyboard/Wiegand-Protocol-Library-for-Arduino
 * Implemented by Greg Doerr (https://github.com/gdoerr)
 *
 * In my example, hooked to an Olimex ESP32-POE device connected to a Retekess H1EM-W
 * Wiegand keypad. This device calls a service on Home Assistant when a code is available
 *
 * Samples key presses every 200ms and stores the key in a string until:
 *   [1] - the user presses a '#' which sends the code immediately
 *   [2] - the user presses nothing for 2,000ms (inter-digit timer) which then sends the code
 *   [3] - the user presses a '*' which sends the '*' immediately
 */
class WiegandReader : public PollingComponent, public CustomAPIDevice {

public:
    WiegandReader(int pinD0, int pinD1, std::string serviceName)
        : PollingComponent(200),
        pinD0(pinD0), pinD1(pinD1),
        serviceName(serviceName) {
    }

    /**
     * Initial setup
     */
    void setup() override {
        _lastWiegand = 0;
        _cardTempHigh = 0;
        _cardTemp = 0;
        _code = 0;
        _wiegandType = 0;
        _bitCount = 0;

        // Configure the input pins
        pinMode(pinD0, INPUT);
        pinMode(pinD1, INPUT);

        // Attach the interrupts
        attachInterrupt(digitalPinToInterrupt(pinD0), ReadD0, FALLING);  // Hardware interrupt - high to low pulse
        attachInterrupt(digitalPinToInterrupt(pinD1), ReadD1, FALLING);  // Hardware interrupt - high to low pulse
    }

    void update() override {
        // See if we have a valid code
        noInterrupts();
        bool rc = DoWiegandConversion();
        interrupts();

        if(rc) {
            if(_code < 10) {
                // We have a digit, make it ASCII for convenience
                keyCodes += (_code + 0x30);
            } else if(_code == 11) {
                // The user pressed '#', send the accumulated code and reset for the next string
                callHAService(keyCodes);
                keyCodes = "";
            } else if(_code == 10) {
                // The user pressed '*', clear the code and send the asterisk
                callHAService("*");
                keyCodes = "";
            }
            // Capture the last time we received a code
            lastCode = millis();
        } else {
            if(keyCodes.length() > 0) {
                // We have a keyCode, see if the interdigit timer expired
                if(millis() - lastCode > 2000) {
                    // The interdigit timer expired, send the code and reset for the next string
                    callHAService(keyCodes);
                    keyCodes = "";
                }
            }
        }
    }

private:
    static volatile unsigned long _cardTempHigh;
    static volatile unsigned long _cardTemp;
    static volatile unsigned long _lastWiegand;
    static volatile int _bitCount;
    static int _wiegandType;
    static unsigned long _code;

    unsigned long lastCode = 0;
    std::string keyCodes = "";

    int pinD0;
    int pinD1;
    std::string serviceName;

    /**
     * Calls a Home Assistant service with the key code
     * @param keyCode
     */
    void callHAService(std::string keyCode) {
        call_homeassistant_service(serviceName.c_str(), {
                {"code", keyCode.c_str()}
        });
    }

    /**
     * D0 Interrupt Handler
     */
    static void ReadD0() {
        _bitCount++;                // Increment bit count for Interrupt connected to D0
        if(_bitCount > 31) {        // If bit count more than 31, process high bits
            _cardTempHigh |= ((0x80000000 & _cardTemp)>>31);    //  shift value to high bits
            _cardTempHigh <<= 1;
            _cardTemp <<=1;
        } else
            _cardTemp <<= 1;        // D0 represent binary 0, so just left shift card data

        _lastWiegand = millis();    // Keep track of last wiegand bit received
    }

    /**
     * D1 Interrupt Handler
     */
    static void 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 to high bits
            _cardTempHigh <<= 1;
            _cardTemp |= 1;
            _cardTemp <<=1;
        } else {
            _cardTemp |= 1;         // D1 represent binary 1, so OR card data with 1 then
            _cardTemp <<= 1;        // left shift card data
        }
        _lastWiegand = millis();    // Keep track of last wiegand bit 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==26)                              // EM tag
            return (*codelow & 0x1FFFFFE) >>1;

        if (bitlength==34)                              // Mifare
        {
            *codehigh = *codehigh & 0x03;               // only need the 2 LSB of the codehigh
            *codehigh <<= 30;                           // shift 2 LSB to MSB
            *codelow >>=1;
            return *codehigh | *codelow;
        }
        return *codelow;                                // EM tag or Mifare without parity bits
    }

    /**
     * Convert the received bitstream
     * @return
     */
    bool DoWiegandConversion () {
        unsigned long cardID;
        unsigned long sysTick = millis();

        if ((sysTick - _lastWiegand) > 25)                              // if no more signal coming through after 25ms
        {
            if ((_bitCount==24) || (_bitCount==26) || (_bitCount==32) || (_bitCount==34) || (_bitCount==8) || (_bitCount==4)) {     // bitCount for keypress=4 or 8, Wiegand 26=24 or 26, Wiegand 34=32 or 34
                _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 32 bits, shift high bits right to make adjustment
                    _cardTempHigh >>= 1;

                if (_bitCount==8) {         // keypress wiegand 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 = (_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 just
                    // 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;
unsigned long WiegandReader::_code = 0;
int WiegandReader::_wiegandType = 0;
pricard32 commented 4 years ago

Wow @gdoerr , that's a greate job you did! Thanks ! Since i'm pretty new to this, and i've buy the exact same keypad as you, can you help me with something more. I'll like to push the data into a text_sensor: can you explain me what I have to that to the yaml and .h file?

Thanks again!

pricard32 commented 4 years ago

And i'm using a ESP-32 for now, can I use any GPIO pin ?

gdoerr commented 4 years ago

You'd have to restructure the Wiegand class into a TextSensor. I don't recommend this because you're not interested in state, you really want the event which is why I call a service. You should be able to find the example code on esphome to implement a custom text sensor.

You should be able to use any GPIO pins on the ESP32 but the order is important...swap D0 and D1 if your're getting weird results.

Good Luck!

pricard32 commented 4 years ago

What would you recommend to write in the script to call to received as a event ?

gdoerr commented 4 years ago

You can use either a yaml script or a python script.

I use a python script to validate the code sequences against a dedicated outlook.com calendar to control access to my garage.

pricard32 commented 4 years ago

Or maybe only share your how implentation, that's help me reverse engineering and understand or to implement it for my use.

pricard32 commented 4 years ago

I've tried to change the class declaration from CustomApiDevice to TextSensor and add PublishState(keyCode) to CallHaService without success

gdoerr commented 4 years ago

@pricard32 Rather than mucking up this feature request thread with a specific problem, perhaps you can post your question on Stack Overflow which is better suited and then post a link to the problem here.

I'm happy to help but you'll need to provide more information than you tried something without success. When you ask the question, be sure and post exactly what you tried and what errors you're getting. Otherwise it's difficult to help you without writing the software for you...

Zebble commented 4 years ago

@gdoerr great work! Solves the immediate problem and really helped me understand how custom components work in esphome. Well done, and thank you.

pricard32 commented 4 years ago

@gdoerr Right, I understand. I'm pretty new here. In fact, the way you use the keypad is exactly how I need it, except I have to found a way to received the keycode in a sensor, or text input, or any kind of event that i'll use late in an automation. This is why I was a little excited when I saw that you've sucessfully implement it.

I'm a Lua programmer and when I try to modify C++ code, it always end with synthax error. I need practice.

So my goal is this : received the keycode to use it in automation without having to write another script in a language that I don't know.

Luisiado commented 4 years ago

hello, first of all thank for your code I test it and works fine, just I would add to the description:

esphome:
  includes:
    - wiegand_device.h

I am trying to catch the code in a python script. my ESPhome yaml is:

custom_component:
  - lambda: |-
       auto wiegand = new WiegandReader(21, 23, "python_scripts.door_mqtt_publish.py");
       return {wiegand};

in config/python_scripts/door_publish_mqtt.py I have a simple code just to test it

logger.info("PYTHON SCRIPT CALLED")
name = data.get('code')
logger.info("HELLOOOOOOOOO %s", name)
hass.bus.fire(name, {"test": "from a Python script!"}
)

and I think that services is not mandatory config/python_scripts/services.yaml

door_mqtt_publish:
  description: publish a rfid code
  field:
    code:
      description: code format
      example: 9451293

In the ESP home logger I can see that the code is read perfectly but I can not trigger the python script to send the payload with the code on it. After read a code nothing appears in the HA logger.

HA logger:

2020-02-03 12:25:05 WARNING (MainThread) [homeassistant.loader] You are using a custom integration for hacs which has not been tested by Home Assistant. This component might cause stability problems, be sure to disable it if you do experience issues with Home Assistant. 2020-02-03 12:25:05 WARNING (MainThread) [homeassistant.loader] You are using a custom integration for customizer which has not been tested by Home Assistant. This component might cause stability problems, be sure to disable it if you do experience issues with Home Assistant. 2020-02-03 12:25:36 ERROR (MainThread) [homeassistant.components.websocket_api.http.connection.1908784496] Error handling message: Unauthorized

gdoerr commented 4 years ago

You cannot call a python script directly through Home Assistant (see here). You have to specifically allow each script (likely for security reasons) and call them through a service as was implemented in the example I posted.

pricard32 commented 4 years ago

When I connect the Wiegand device to the GPIO of the ESP32, the board failed to boot every time. Someone have a clue why? (for now it connetected to 21/23, but i've try with other pins too)

CountParadox commented 4 years ago

Check the power connections on the device

On Sun, 1 Mar 2020, 1:31 pm Paul Ricard, notifications@github.com wrote:

When I connect the Wiegand device to the GPIO of the ESP32, the board failed to boot every time. Someone have a clue why? (for now it connetected to 21/23, but i've try with other pins too)

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/esphome/feature-requests/issues/211?email_source=notifications&email_token=ABP5BVFYHXGJ3EGUF55D563RFHCHJA5CNFSM4HKTC32KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOENMQNFI#issuecomment-593036949, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABP5BVHPQRPURUCOEYE2PFTRFHCHJANCNFSM4HKTC32A .

pricard32 commented 4 years ago

The power is correct, since when I disconnect the PIN between the ESP and the keypad, the board boot as usual

Luisiado commented 4 years ago

do you have grounds (RFID, ESP32, power supply) short circuited? all grounds should be connected between themselves.

On Sun, Mar 1, 2020 at 11:56 PM Paul Ricard notifications@github.com wrote:

The power is correct, since when I disconnect the PIN between the ESP and the keypad, the board boot as usual

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/esphome/feature-requests/issues/211?email_source=notifications&email_token=AL5SHDVHAPGVMSC5VYEOHWTRFLR2FA5CNFSM4HKTC32KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOENNN7CI#issuecomment-593158025, or unsubscribe https://github.com/notifications/unsubscribe-auth/AL5SHDSKVR4CXC7Z7SRONPTRFLR2FANCNFSM4HKTC32A .

pricard32 commented 4 years ago

@Luisiado i'm ashamed... ! Forgot the ground between the ESP and the keypad and that cause the ESP to fail to boot... ! Thanks !

pricard32 commented 4 years ago

@Luisiado what did you do to solve your issue? how did you publish the code via MQTT to HA ?

gio-dot commented 4 years ago

@Luisiado what did you do to solve your issue? how did you publish the code via MQTT to HA ?

Hi, what kind of ESP do you have? It is 3.3V right? What is your wiegand device? It is 5.0V on Do and D1 lines right? Did you use any level shifter to connect wiegand device to ESP device?

Luisiado commented 4 years ago

There are some articles which say that ESP is 5V tolerant. The proper way to connect the rfid reader is through voltage divider. I am not using the voltage divider and my system is working fine but I can't recommend you to do the same ;)

On Mon, Mar 2, 2020 at 5:28 PM Giovanni notifications@github.com wrote:

@Luisiado https://github.com/Luisiado what did you do to solve your issue? how did you publish the code via MQTT to HA ?

Hi, what kind of ESP do you have? It is 3.3V right? What is your wiegand device? It is 5.0V on Do and D1 lines right? Did you use any level shifter to connect wiegand device to ESP device?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/esphome/feature-requests/issues/211?email_source=notifications&email_token=AL5SHDQF7JLDYVF6PVWN4J3RFPND7A5CNFSM4HKTC32KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOENP676Y#issuecomment-593489915, or unsubscribe https://github.com/notifications/unsubscribe-auth/AL5SHDQPKWIFLHFXBC4MLRLRFPND7ANCNFSM4HKTC32A .

gio-dot commented 4 years ago

Ok, thank you. I'm totally new on esphome and i don't know pyton. I know quite well yaml. Problem is that i have no idea how script to catch the code should be. Did you manage to have it working?

nerdosity commented 4 years ago

There are some articles which say that ESP is 5V tolerant. The proper way to connect the rfid reader is through voltage divider. I am not using the voltage divider and my system is working fine but I can't recommend you to do the same ;) On Mon, Mar 2, 2020 at 5:28 PM Giovanni @.***> wrote: @Luisiado https://github.com/Luisiado what did you do to solve your issue? how did you publish the code via MQTT to HA ? Hi, what kind of ESP do you have? It is 3.3V right? What is your wiegand device? It is 5.0V on Do and D1 lines right? Did you use any level shifter to connect wiegand device to ESP device? — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub <#211?email_source=notifications&email_token=AL5SHDQF7JLDYVF6PVWN4J3RFPND7A5CNFSM4HKTC32KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOENP676Y#issuecomment-593489915>, or unsubscribe https://github.com/notifications/unsubscribe-auth/AL5SHDQPKWIFLHFXBC4MLRLRFPND7ANCNFSM4HKTC32A .

I can testify that it's 5v tolerant, I'm using it this way, even esp8266 was. It's semi-official, since an engineer from Expressif commented this issue on Facebook saying that it was tolerant.

Luisiado commented 4 years ago

I opened a issue here but I didn´t get answer. Maybe now we are lucky because I published the code. https://community.home-assistant.io/t/how-to-read-a-variable-from-a-custom-component-wiegand/168588

On Mon, Mar 2, 2020 at 7:30 PM Giovanni notifications@github.com wrote:

Ok, thank you. I'm titally new on esphome and i don't know pyton. I know quite well yaml. Problem is that i have no idea how script to catch the code should be. Did you manage to have it working?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/esphome/feature-requests/issues/211?email_source=notifications&email_token=AL5SHDTGBHBD3LCNUTGDRM3RFP3NHA5CNFSM4HKTC32KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOENQNM5Q#issuecomment-593548918, or unsubscribe https://github.com/notifications/unsubscribe-auth/AL5SHDWI2TLAELEEGZ2FOYLRFP3NHANCNFSM4HKTC32A .

gio-dot commented 4 years ago

@Luisiado i'm ashamed... ! Forgot the ground between the ESP and the keypad and that cause the ESP to fail to boot... ! Thanks !

Hi, i can't understand: gdoerr code works only with wiegand keyboards or even with wiegand rfid tag readers? I'm trying it wth an rfid tag reader but i can't see nothing in esp log when i read a tag. My reader works in an arduino system...

pricard32 commented 4 years ago

@Gio-dot The rfid tag reader doesnt seems to work for me too.

gio-dot commented 4 years ago

@Gio-dot The rfid tag reader doesnt seems to work for me too. This is my setup. I use an ESP32 (D0 pin 23 D1 pin 21). When i read a tag nothing happens in the log... photo_2020-03-31_17-35-19

gio-dot commented 4 years ago

Save the code below as wiegand_device.h and include it as a custom component in your YAML file.

custom_component:
  - lambda: |-
       auto wiegand = new WiegandReader(pin0, pin1, "script.to_call");
       return {wiegand};

pin0 should be the GPIO connected to the D0 signal and pin1 is the GPIO connected to the D1 signal. Replace "script.to_call" with the name of the script or service to call. The code entered will be passed as the parameter code.

#include "esphome.h"

/**
 * Wiegand Reader Custom Device
 *
 * Copied from https://github.com/monkeyboard/Wiegand-Protocol-Library-for-Arduino
 * Implemented by Greg Doerr (https://github.com/gdoerr)
 *
 * In my example, hooked to an Olimex ESP32-POE device connected to a Retekess H1EM-W
 * Wiegand keypad. This device calls a service on Home Assistant when a code is available
 *
 * Samples key presses every 200ms and stores the key in a string until:
 *   [1] - the user presses a '#' which sends the code immediately
 *   [2] - the user presses nothing for 2,000ms (inter-digit timer) which then sends the code
 *   [3] - the user presses a '*' which sends the '*' immediately
 */
class WiegandReader : public PollingComponent, public CustomAPIDevice {

public:
    WiegandReader(int pinD0, int pinD1, std::string serviceName)
        : PollingComponent(200),
        pinD0(pinD0), pinD1(pinD1),
        serviceName(serviceName) {
    }

    /**
     * Initial setup
     */
    void setup() override {
        _lastWiegand = 0;
        _cardTempHigh = 0;
        _cardTemp = 0;
        _code = 0;
        _wiegandType = 0;
        _bitCount = 0;

        // Configure the input pins
        pinMode(pinD0, INPUT);
        pinMode(pinD1, INPUT);

        // Attach the interrupts
        attachInterrupt(digitalPinToInterrupt(pinD0), ReadD0, FALLING);  // Hardware interrupt - high to low pulse
        attachInterrupt(digitalPinToInterrupt(pinD1), ReadD1, FALLING);  // Hardware interrupt - high to low pulse
    }

    void update() override {
        // See if we have a valid code
        noInterrupts();
        bool rc = DoWiegandConversion();
        interrupts();

        if(rc) {
            if(_code < 10) {
                // We have a digit, make it ASCII for convenience
                keyCodes += (_code + 0x30);
            } else if(_code == 11) {
                // The user pressed '#', send the accumulated code and reset for the next string
                callHAService(keyCodes);
                keyCodes = "";
            } else if(_code == 10) {
                // The user pressed '*', clear the code and send the asterisk
                callHAService("*");
                keyCodes = "";
            }
            // Capture the last time we received a code
            lastCode = millis();
        } else {
            if(keyCodes.length() > 0) {
                // We have a keyCode, see if the interdigit timer expired
                if(millis() - lastCode > 2000) {
                    // The interdigit timer expired, send the code and reset for the next string
                    callHAService(keyCodes);
                    keyCodes = "";
                }
            }
        }
    }

private:
    static volatile unsigned long _cardTempHigh;
    static volatile unsigned long _cardTemp;
    static volatile unsigned long _lastWiegand;
    static volatile int _bitCount;
    static int _wiegandType;
    static unsigned long _code;

    unsigned long lastCode = 0;
    std::string keyCodes = "";

    int pinD0;
    int pinD1;
    std::string serviceName;

    /**
     * Calls a Home Assistant service with the key code
     * @param keyCode
     */
    void callHAService(std::string keyCode) {
        call_homeassistant_service(serviceName.c_str(), {
                {"code", keyCode.c_str()}
        });
    }

    /**
     * D0 Interrupt Handler
     */
    static void ReadD0() {
        _bitCount++;              // Increment bit count for Interrupt connected to D0
        if(_bitCount > 31) {      // If bit count more than 31, process high bits
            _cardTempHigh |= ((0x80000000 & _cardTemp)>>31);  //  shift value to high bits
            _cardTempHigh <<= 1;
            _cardTemp <<=1;
        } else
            _cardTemp <<= 1;      // D0 represent binary 0, so just left shift card data

        _lastWiegand = millis();  // Keep track of last wiegand bit received
    }

    /**
     * D1 Interrupt Handler
     */
    static void 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 to high bits
            _cardTempHigh <<= 1;
            _cardTemp |= 1;
            _cardTemp <<=1;
        } else {
            _cardTemp |= 1;           // D1 represent binary 1, so OR card data with 1 then
            _cardTemp <<= 1;      // left shift card data
        }
        _lastWiegand = millis();  // Keep track of last wiegand bit 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==26)                                // EM tag
            return (*codelow & 0x1FFFFFE) >>1;

        if (bitlength==34)                                // Mifare
        {
            *codehigh = *codehigh & 0x03;             // only need the 2 LSB of the codehigh
            *codehigh <<= 30;                         // shift 2 LSB to MSB
            *codelow >>=1;
            return *codehigh | *codelow;
        }
        return *codelow;                              // EM tag or Mifare without parity bits
    }

    /**
     * Convert the received bitstream
     * @return
     */
    bool DoWiegandConversion () {
        unsigned long cardID;
        unsigned long sysTick = millis();

        if ((sysTick - _lastWiegand) > 25)                                // if no more signal coming through after 25ms
        {
            if ((_bitCount==24) || (_bitCount==26) || (_bitCount==32) || (_bitCount==34) || (_bitCount==8) || (_bitCount==4)) {   // bitCount for keypress=4 or 8, Wiegand 26=24 or 26, Wiegand 34=32 or 34
                _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 32 bits, shift high bits right to make adjustment
                    _cardTempHigh >>= 1;

                if (_bitCount==8) {       // keypress wiegand 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 = (_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 just
                    // 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;
unsigned long WiegandReader::_code = 0;
int WiegandReader::_wiegandType = 0;

I gdoerr, can you help to make it working? I used your code but nothing happen when i read a tag. The same reader work well when connected to an arduino with the monkeyboard library. Thank you.

gdoerr commented 4 years ago

@Gio-dot Since your keypad works with your Arduino I assume it's either the hardware interface (you don't mention the board you're using) or with the Home Assistant service call.

I suggest adding some log statements in the update loop and see if you're getting anything in that loop. That will narrow down if it's a keypad interface or Home Assistant interface issue.

Good luck

gio-dot commented 4 years ago

@Gio-dot Since your keypad works with your Arduino I assume it's either the hardware interface (you don't mention the board you're using) or with the Home Assistant service call.

I suggest adding some log statements in the update loop and see if you're getting anything in that loop. That will narrow down if it's a keypad interface or Home Assistant interface issue.

Good luck

I'm using an esp32 with a wiegand id tag reader like the picture 2 post above. Which esp are you using? Can you suggest how ha script should be? I never used a script that manage a parameter until now.. Thank you.