Home Assistant integrated HID iclass SE

The challenge for this portion was the janky undocumented prototype shield I used. Would have thought the grounds on the proto were all tied together. Nope, had to jump a bunch of shit.

Also, frustrating how 1 single strand of 22awg wire can cause a short and make things not work. Thank FSM for continuity beeps on multimeters.

The new Arduino sketch. To allow me to easily publish this, I created an arduino_secrets.h file. All of the variables set to SECRET_SOMETHING, the SECRET_SOMETHING is #define in the arduino_secrets.h file.

#include <Wiegand.h>
#include <PubSubClient.h>
#include <WiFiEsp.h>
#ifndef HAVE_HWSERIAL1
#include "SoftwareSerial.h"
// Declare pins for 8266 serial communication
SoftwareSerial Serial1(7, 6); // RX, TX
#endif

// Include secrets file DEFINE all sensitive information
#include "arduino_secrets.h"

// These are the pins connected to the Wiegand D0 and D1 signals.
#define PIN_D0 2
#define PIN_D1 3

//Array of authorized card
const char* AuthorizedCards[] = {SECRET_CARD_0,SECRET_CARD_1};
const int numAuth = 2;

// WIFI Variables
char ssid[] = SECRET_SSID;            // your network SSID (name)
char pass[] = SECRET_WIFI_PASS;        // your network password
int status = WL_IDLE_STATUS;     // the Wifi radio's status


//MQTT server and creds
const char* server = SECRET_MQTT_SERVER;
const char* username = SECRET_MQTT_USER;
const char* password = SECRET_MQTT_PASS;
const char* UID = "arduino-2";
const char* mqttTopic = "rfid/side_door";

// The object that handles the wiegand protocol
Wiegand wiegand;

// Handler for MQTT received messages
void callback(char* topic, byte* payload, unsigned int length) {
  // handle message arrived
  // This software doesn't listen to MQTT
  // so this function is left empty
}

// Initialize ethernet and mqtt clients
WiFiEspClient ethClient;
PubSubClient client(server, 1883, callback, ethClient);

// Function to reconnect to MQTT is connection has dropped
boolean reconnect() {
  Serial.println("Reconnecting to MQTT server.");
  if (client.connect(UID, username, password)){
    Serial.println("Connected to MQTT");
  } else {
    Serial.println("Failed to connect to MQTT");
  }
}

// Initialize Wiegand reader
void setup() {
  Serial.begin(9600);

  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  // initialize serial for ESP module
  Serial1.begin(9600);

  //initialize pins as INPUT
  pinMode(PIN_D0, INPUT_PULLUP);
  pinMode(PIN_D1, INPUT_PULLUP);

  // initialize ESP module
  WiFi.init(&Serial1);

    // check for the presence of the shield
  if (WiFi.status() == WL_NO_SHIELD) {
    Serial.println("WiFi shield not present");
    // don't continue
    while (true);
  }

  // attempt to connect to WiFi network
  while ( status != WL_CONNECTED) {
    Serial.print("Attempting to connect to WPA SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network
    status = WiFi.begin(ssid, pass);
  }
  Serial.println("You're connected to the network");

  // Install listeners and initialize Wiegand reader
  wiegand.onReceive(receivedData, "Card readed: ");
  wiegand.onReceiveError(receivedDataError, "Card read error: ");
  wiegand.onStateChange(stateChanged, "State changed: ");
  wiegand.begin(Wiegand::LENGTH_ANY, false);

  // Initial connection to MQTT server
  reconnect();
}

// Continuously checks for pending messages and polls updates from the wiegand inputs
void loop() {
  // Checks for pending messages
  wiegand.flush();

  // Check for changes on the the wiegand input pins
  wiegand.setPin0State(digitalRead(PIN_D0));
  wiegand.setPin1State(digitalRead(PIN_D1));
}

// Notifies when a reader has been connected or disconnected.
// Instead of a message, the seconds parameter can be anything you want -- Whatever you specify on `wiegand.onStateChange()`
void stateChanged(bool plugged, const char* message) {
    Serial.print(message);
    Serial.println(plugged ? "CONNECTED" : "DISCONNECTED");
}

// Notifies when a card was read.
// Instead of a message, the seconds parameter can be anything you want -- Whatever you specify on `wiegand.onReceive()`
void receivedData(uint8_t* data, uint8_t bits, const char* message) {
    String CardCode = "";
    //Print value in HEX
    uint8_t bytes = (bits+7)/8;
    for (int i=bytes-1; i>=0; i--) {
      // Copy HEX to variable CardCode
      String FirstNum = (String (data[i] >> 4, 16));
      String SecondNum = (String (data[i] & 0xF, 16));
      CardCode = (CardCode + FirstNum + SecondNum);
    }

    // Call authentication function with scanned card
    Serial.print("Scanned ");
    Serial.println(CardCode);
    authenticateCard(CardCode);
}

void authenticateCard(String CardHex) {
  // Loop through authorized cards to see if scanned card is authorized
  for (int i=0;i<numAuth;i++){
    if ( CardHex == AuthorizedCards[i] ) {
      // Card is authorized
      Serial.print(AuthorizedCards[i]);
      Serial.println(" is authorized.");

      // Reconnect to MQTT server if connection dropped
      if (!client.connected()) {
        Serial.println("MQTT not connected.");
        if (!reconnect()){
          Serial.println("Unable to connect to MQTT");
        } else {
          // Publish unlock message to MQTT broker
          client.publish(mqttTopic, "unlock");   
        }
      }

      //Exit loop since we've found the card
      i=numAuth;
    }
  }
}

// Notifies when an invalid transmission is detected
void receivedDataError(Wiegand::DataError error, uint8_t* rawData, uint8_t rawBits, const char* message) {
    Serial.print(message);
    Serial.print(Wiegand::DataErrorStr(error));
    Serial.print(" - Raw data: ");
    Serial.print(rawBits);
    Serial.print("bits / ");

    //Print value in HEX
    uint8_t bytes = (rawBits+7)/8;
    for (int i=0; i<bytes; i++) {
        Serial.print(rawData[i] >> 4, 16);
        Serial.print(rawData[i] & 0xF, 16);
    }
    Serial.println();
}
1 Like

Parts list:
Arduino Uno R3 ($22)
HID iClass SE ($20 used)
AMS1117 5v to 3v buck converter ($1)
50μF Capacitor ($0.01)
5v to 3v logic converter ($3)
ESP-01s 8266 ($7)
Adjustable Buck Converter (12v stepped down to 7v) ($3.25)
12v 1amp Wall Wart ($3.50)
22AWG Shield cable. Minimum 4 conductor. I used 8 because wire breaks happen and it’s easier to just use a different conductor vs running new wire. ($40 for 100ft)

I still suck at Fritzing, but here’s the diagram.

12v power comes in through the wall wart. 12v goes directly to the HID iClass.
12v to the adjustable buck converter set to 7v output. 7v goes to Arduino VIN.
HID Wiegand data0 goes to Arduino D2. Data1 to Arduino D3.
5v-3v buck converter takes 5v from Arduino 5v pin. Capacitor bridges output and ground to stabilize output.
3v from buck output goes to 8266 VIN(3.3v). RX and TX pins from the 8266 go to LV1 and LV2 on the logic converter.
Logic converter HV goes to Arduino 5v. LV to Arduino 3.3v. HV1 to Arduino D6. HV2 to Arduino D7.

  • The adjustable buck converter allows us to power the reader off of 12v directly from the wall wart for maximum range. It steps down the voltage to 7v to power the Arduino.
  • The 5v-3v buck converter is required to power the 8266 off of 3.3v. The arduino 3.3v pin does not have enough current to do this, so we step down the 5v Arduino line. The capacitor simply smooths the output.
  • 8266 takes 3.3v logic levels to the RX/TX pins. However, Arduino logic is 5v. So we use a logic converter to bring communication from the 8266 up to 5v for the Arduino to consume, and drops communication down to 3.3v for the 8266 to consume. 5v reference voltage taken from Arduino 5v and 3.3v reference taken from Arduino 3.3v.
  • Wiegand Data0 from the HID goes to Arduino D2. Data1 to Arduino D3.
  • All grounds are common ground!

The code is specific to the HID iClass I have. It’s MSB, so I have the loop running backwards to reverse the card UID to the correct order. For an LSB reader, like a standard AWID or a LSB configured HID, change the for loop in the receivedData function to run the opposite direction. Manipulating this loop will allow this code to work with any reader using the card UID. Prox, Mifare, AWID, EM4100 all work fine!

I assume most people won’t be using a MQTT broker, so simply change the code in the authenticateCard function to do whatever you want. Trip a relay, send an email/sms, it’s pretty trivial once you get the hardware working.

2 Likes

The unit has been working great. A bit of a delay to unlock the door due to the Zwave delay (roughly 1.5 seconds). I haven’t installed the front door reader yet because I have to get under the porch to drill a hole to run the wire.

A neat update though. I was able to wire up a second reader to the same arduino. Minor update to the code, and the 4 channel logic converter conveniently had 2 channels open for the 2 data wires off of the second reader. However, the 12v 1a power supply is dangerously close to maximum load with the arduino and 2 readers. Have to upgrade to a 2a wall wart. But once I install the zwave lock on the back door, I’ll be able to run the side and back door off of the same unit, and the front door off of another unit.

In theory, I could run all 3 readers off of the 1 unit, but would need to upgrade the logic converter to 6 channel minimum. And the wire run from the front door would be pretty long.

1 Like