[SOLVED] Using wireless to eliminate wiegand wiring

I’ve recently finished the firmware for a wireless access control pushbutton, with the help of @Moors7, and @harrisonhjones, it’s very basic and uses the buddy publish/subscribe to activate the relay shield on a second photon. Now my boss would like to know if it would be possible to have a cardreader and one photon to read the 5 digit wiegand card number, publish it, and have another photon connected to the controller to verify the card, then send a signal back to the first to unlock the door.

So I have a 2 part problem:

  1. I know there is a wiegand library to read the card number, how would I take the data from that library, and get it to the second photon?

  2. how to get the data from the second photon to my controller?

Yup, that is possible :wink:

  1. Use the data payload field of Particle.publish()
  2. What controller is your controller? What interfaces does it have?

I would do something like this:

  1. Photon A, connected to the reader, waits for cards to be read. Once read it sends out an event, using Publish as @ScruffR suggests. So something like Particle.publish('READ_CARD',{card #});
  2. Photon B, connected to your controller subscribes to the READ_CARD event using Particle.subscribe(...). Once it retrieves an event it sends it to the controller. The controller turns around, validates it, and then sends a “accept” or “deny” message. If “accept”, Photon B publishes a “OPEN_THE_GATES” event.
  3. Photon C, which is subscribed to “OPEN_THE_GATES” events retrieves that message and activates the relay.
2 Likes

Thats exactly what I had in mind, I’ve used the publish/subscribe functions, what I’m not sure about is how to send the card number from “Photon B” to my controller, would I just “Print” the number using the serial pins?

// RFID_UART.ino

#if defined (SPARK)
#include "SeeedRFID/SeeedRFID.h"
#else
#include <SoftwareSerial.h>
#include <SeeedRFID.h>
#endif

#define RFID_RX_PIN 10
#define RFID_TX_PIN 11

// #define DEBUGRFID
#define TEST

SeeedRFID RFID(RFID_RX_PIN, RFID_TX_PIN);
RFIDdata tag;

void setup() {
	Serial1.begin(9600);	//Done here to prevent SeeedRFID constructor system crash
	
	Serial.begin(57600);
	Serial.println("Hello, double bk!");
}

void loop() { 
	if(RFID.isAvailable()){
		tag = RFID.data();
		Serial.print("RFID card number: ");
		Serial.println(RFID.cardNumber());
#ifdef TEST
	Serial.print("RFID raw data: ");
	for(int i=0; i<tag.dataLen; i++){
	    Serial.print(tag.raw[i], HEX);
	    Serial.print('\t');
		}
#endif
	}
}

Would you guys agree that “tag” would be my card number in this lib?

For a controller I’m using a Doorking 1835 or 1838, depending on the job. As an interface, I’m not sure what you’re asking, it has wiegand reading capability built in, and it is 4 wires, (red:12VDC+, black: ground&common, white:data1, green:data0).

Unless you want to re-implement Wiegand on the Photon you are going to want a different interface. Does that controller have a serial interface? If not, you will need to write a Wiegand writer. Doesn’t look too hard as Weigand is well documented. You’d have the Photon B send the card data to the controller via Wiegand.

The controller does have an RS232 connection but it’s strictly for programming, nothing I can do about that. I’ll see if I can do some research on a wiegand writer. What are the best options for porting one over if I find it with arduino or somewhere else?

I thought you were asking how to communicate with the controller, so I just had to ask what controler (which you answered) and what ways of communication (=interface(s)) it exposes (which is implicitly answered by looking at the now known controller datasheet.

1 Like

Unfortunately, I don’t have the knowledge to write a writer, however, there is one already written for Arduino, everyones little board that could, it has been given freely, but I am not sure where to start in converting it to work with the photon since I have never even used an Arduino board.

int i;
int reader1[30];
int readerTmp[30];
volatile int reader1Count = 0;

void reader1One(void) {
  reader1[reader1Count] = 1;
  reader1Count++;
}

void reader1Zero(void) {
reader1[reader1Count] = 0;
  reader1Count++;
}


void setup()
{
  Serial.begin(57600);
  // Attach pin change interrupt service routines from the Wiegand RFID readers
  attachInterrupt(0, reader1Zero, FALLING);//DATA0 to pin 2
  attachInterrupt(1, reader1One, FALLING); //DATA1 to pin 3
  delay(10);
  
  //Set pins 10/D0 and 11/D1 to output for re transmission
   pinMode(10, OUTPUT);
   pinMode(11, OUTPUT);
   
   digitalWrite(10, HIGH);
   digitalWrite(11, HIGH);
  
  
  // the interrupt in the Atmel processor mises out the first negitave pulse as the inputs are already high,
  // so this gives a pulse to each reader input line to get the interrupts working properly.
  // Then clear out the reader variables.
  // The readers are open collector sitting normally at a one so this is OK
  for(int i = 2; i<4; i++){
  pinMode(i, OUTPUT);
   digitalWrite(i, HIGH); // enable internal pull up causing a one
  digitalWrite(i, LOW); // disable internal pull up causing zero and thus an interrupt
  pinMode(i, INPUT);
  digitalWrite(i, HIGH); // enable internal pull up
  }
  delay(10);
  // put the reader input variables to zero
  for (i = 0; i < 30; i = i + 1) {
  reader1[i] = 0;
}
  reader1Count = 0;
  digitalWrite(13, HIGH);  // show Arduino has finished initilisation
}

void loop() {
  if(reader1Count >= 27){
  Serial.println("A");
  

readerTmp[0] = reader1[25] ;

readerTmp[1] = 0 ;
readerTmp[2] = 0 ;
readerTmp[3] = 0 ;
readerTmp[4] = 0 ;
readerTmp[5] = 0 ;
readerTmp[6] = 0 ;
readerTmp[7] = 0 ;
readerTmp[8] = 0 ;

readerTmp[9] = 0 ;
readerTmp[10] = 0 ;
readerTmp[11] = 0 ;
readerTmp[12] = 0 ;
readerTmp[13] = 0 ;
readerTmp[14] = 0 ;
readerTmp[15] = 0 ;
readerTmp[16] = 0 ;
readerTmp[17]= 0 ;
readerTmp[18] = 0 ;
readerTmp[19] = 0 ;
readerTmp[20] = 0 ;
readerTmp[21] = 0 ;
readerTmp[22] = 0 ;
readerTmp[23] = 0 ;
readerTmp[24] = 0 ;

readerTmp[25] =  reader1[0] ;

//If sum of segments 2 to 13 even then First parity 1
if ((readerTmp[1] + readerTmp[2] + readerTmp[3] + readerTmp[4] + readerTmp[5] + readerTmp[6] + readerTmp[7] + readerTmp[8] + readerTmp[9] + readerTmp[10] + readerTmp[11] + readerTmp[12]) % 2){
readerTmp[0] = 1;
  Serial.println("even = odd");
}else{
readerTmp[0] = 0;
  Serial.println("even = even");
}


//If sum of segments 14 to 25 even then last parity 1
if ((readerTmp[13] + readerTmp[14] + readerTmp[15] + readerTmp[16] + readerTmp[17] + readerTmp[18] + readerTmp[19] + readerTmp[20] + readerTmp[21] + readerTmp[22] + readerTmp[23] + readerTmp[24]) % 2){
readerTmp[25] = 0;
Serial.println("odd = odd");
}else{
readerTmp[25] = 1;
Serial.println("odd = even");
}


for (i = 0; i < 26; i = i + 1) {

  if(readerTmp[i] == 0){
    delay(2);
  digitalWrite(10, LOW);
  delayMicroseconds(50);
  digitalWrite(10, HIGH); 
  }else{
   delay(2);
  digitalWrite(11, LOW);
  delayMicroseconds(50);
  digitalWrite(11, HIGH); 
  }
  
  Serial.print(readerTmp[i]);
  readerTmp[i] = 0;
}

Serial.println("");
  reader1Count = 0;
     }    
}

Any help would be greatly appreciated, as always.

There's a bit of code here referenced for Arduino to reverse engineer the Wiegand protocols, which should be useful:

http://blog.opensecurityresearch.com/2012/12/hacking-wiegand-serial-protocol.html

The corresponding Arduino code lives as part of this repository:

for routines for a VertX V2000 door controller.

1 Like

That code is not nice, but should be easy to port.
I’ll give it a quick and dirty fix when I’ve got time.

Update:
But at a closer look that loop() code doesn’t make a lot of sense :confused:
Where can the original be seen?

http://forum.arduino.cc/index.php?topic=87582.0

Heres the link to the forum where its posted, it is post #4.

The code you posted was actually post #9 which was severely bodged by the poster, who had a better, but apparently still not working correctly, code in post #8.
So without having the hardware I can just port the sort-of working code for Photon, but can’t test any of it.

What of that code do you actually need?
As I read the code, the device should act as a card reader (via the two interrupt pins) and then pass the read signals on via the two output pins.
Do you actually need that?

No, what I need to do, is pull the card number from the cloud, which was transmitted by another photon connected to a cardreader, and transmit it to my controller. Is it possible to inspect the card number by each digit coming in? Then, in theory, I could pull one pin low for zero, and a second pin low for one.

So you already can read the Wiegand card with one Photon and got the ID?
Then you can just send that number as string via Particle.publish(), receive that string, convert it back into a number and push out the bits.
But exactly that pushing bits is the tricky part as it needs to follow the Wiegand protocol (including parity bits).

1 Like

Exactly, I can read the card number but it looks like everyone who has worked with wiegand wants to use the photon as a controller, which is fine, but the system I use allows real time tracking and programming remotely, and a lot of other features.

As I understand the Wiegand 26 code, you'd not only need to transmit the card number (00000~65535) but also the facility number (000~255) in order to get a correct match with your controller.
But once all required data is available communicating that ID to the controller shouldn't be too hard, but what did you mean by that

How would you communicate the result of the verification from the controller back to Photon 2 to forward it to Photon 1?

Each photon would have a publish and subscribe function, the first would send the data out, the second would send the number to my controller, when the controller verifies it, it activates a relay which I would tie into D0 and VIN, and publish that function. The second photon would be listening for that and when it is pulled high, activate the relay shield on the second and open the gate. Sorry if this all runs together, I’m on my phone right now.

I see, so D0 would be set as pinMode(D0, INPUT) with en external pull-down resistor to read the controller “response”.
I usually prefer pinMode(D0, INPUT_PULLUP) and shutting down to GND, but the other way round to forget about the voltage limitation and external pull-resistor.

1 Like

I must emphasize that I whipped this up quickly this afternoon and there might be bugs. But it is sort of a proof of concept of this sort of thing.

The HID ProxPoint Plus reader is connected to the top Photon, which reads the Wiegand signals, decodes, and does a Particle.publish. The bottom left Photon subscribes to the events and reconstitutes the signal, which is read by the bottom right Photon.

Reader code:

#include "Particle.h"

const int WIEGAND_D0_PIN = D2; // Green
const int WIEGAND_D1_PIN = D3; // White

void wiegandInterrupt(); // forward declaration
bool decodeWiegand(unsigned long value); // forward declaration

volatile int cardBitCount = 0;
volatile unsigned long cardValue = 0;
volatile unsigned long cardReadStart = 0;

void setup() {
	Serial.begin(9600);

	// Wiegand pulse are narrow, typically 100 microseconds, so interrupts are desirable to accurately
	// detect them
	attachInterrupt(WIEGAND_D0_PIN, wiegandInterrupt, FALLING);
	attachInterrupt(WIEGAND_D1_PIN, wiegandInterrupt, FALLING);
}

void loop() {
	if (cardReadStart != 0) {
		if (cardBitCount == 26) {
			// Got a valid 26-bit card
			if (decodeWiegand(cardValue)) {
				// Is valid
				Particle.publish("card", String(cardValue), PRIVATE);
			}
			cardReadStart = 0;
			cardBitCount = 0;
		}
		else
		if (millis() - cardReadStart > 500) {
			Serial.println("failed to read enough bits");
			cardReadStart = 0;
			cardBitCount = 0;
		}
	}
}

void wiegandInterrupt() {
	if (cardBitCount == 0) {
		cardReadStart = millis();
		cardValue = 0;
	}
	else {
		cardValue <<= 1;
	}

	if (pinReadFast(WIEGAND_D1_PIN) == LOW) {
		cardValue |= 1;
	}
	cardBitCount++;
}

// Note: in the following table the bit numbers are 0-based, 0 - 25 for a 26-bit Wiegand code!
// Bit 25: Even parity, first half of bits
// Bits 24 - 17: Facility code, MSB first 0 - 255
// Bits 16 - 1: Card code, MSB first, 0 - 65536
// Bit 0: Odd parity, second half of bits
bool decodeWiegand(unsigned long value) {
	bool valid = false;

	int facility = (value >> 17) & 0xff;
	int card = (value >> 1) & 0xffff;

	unsigned long tempValue = value;
	int parity = 0;
	for(int ii = 0; ii < 13; ii++) {
		if (tempValue & 1) {
			parity++;
		}
		tempValue >>= 1;
	}
    if ((parity & 0x1) == 1) {
    	// First parity passed
    	parity = 0;
		for(int ii = 0; ii < 13; ii++) {
			if (tempValue & 1) {
				parity++;
			}
			tempValue >>= 1;
		}
		if ((parity & 0x1) == 0) {
			// Second parity passed; looks valid
			Serial.printlnf("value=0x%x facility=%d card=%d", value, facility, card);
			valid = true;
		}
		else {
	    	Serial.printlnf("even parity error value=0x%x", value);
		}
    }
    else {
    	Serial.printlnf("odd parity error value=0x%x", value);
    }
    return valid;
}


Sender (makes Wiegand signals from data):

#include "Particle.h"

#include "SparkIntervalTimer/SparkIntervalTimer.h"

void subscriptionHandler(const char *event, const char *data); // forward declaration
void senderInterrupt(); // forward declaration

const int WIEGAND_D0_PIN = D2; // Green
const int WIEGAND_D1_PIN = D3; // White

const int TIMER_PERIOD_US = 100;
const int LOW_PERIOD = 1;
const int HIGH_PERIOD = 10;

IntervalTimer timer;
int sendBitsLeft = 0;
int sendPeriod = 0;
unsigned long sendValue;

void setup() {
	Serial.begin(9600);

	pinMode(WIEGAND_D0_PIN, OUTPUT);
	digitalWrite(WIEGAND_D0_PIN, HIGH);

	pinMode(WIEGAND_D1_PIN, OUTPUT);
	digitalWrite(WIEGAND_D1_PIN, HIGH);

	timer.begin(senderInterrupt, TIMER_PERIOD_US, uSec);

	Particle.subscribe("card", subscriptionHandler, MY_DEVICES);
}

void loop() {
}

void subscriptionHandler(const char *event, const char *data) {
	unsigned long value = strtoul(data, NULL, 0);
	Serial.printlnf("received value=0x%x", value);

	sendPeriod = 0;
	sendValue = value;
	sendBitsLeft = 26;
}

void senderInterrupt() {
	if (sendBitsLeft > 0) {
		if (sendPeriod == HIGH_PERIOD) {
			sendPeriod = 0;
		}

		if (sendPeriod == 0) {
			// Write out the data bit
			if ((sendValue >> (sendBitsLeft - 1)) & 1) {
				// 1 bit
				digitalWrite(WIEGAND_D1_PIN, LOW);
			}
			else {
				// 0 bit
				digitalWrite(WIEGAND_D0_PIN, LOW);
			}
		}
		else
		if (sendPeriod == LOW_PERIOD) {
			// Restore high
			digitalWrite(WIEGAND_D0_PIN, HIGH);
			digitalWrite(WIEGAND_D1_PIN, HIGH);
			sendBitsLeft--;
		}

		sendPeriod++;
	}
}


4 Likes