iButton hackery
To get in to various places around Oxford, many places use little iButton fobs, which run on the Dallas Semiconductors 1-Wire protocol. When you tap them against a “master” device, they broadcast their unique ID, which the master can then look up in its list of people.
Kevin and I decided that this was excessively simple, and that we should build something to read and impersonate these fobs. Fortunately, the 1-Wire protocol they use is relatively standard, and there’s already an Arduino library to handle them.
The circuit you need is simple: connect some pin to a 4.7k resistor, +5v power and the centre of the fob, and ground the outside. Then run the following and the serial monitor should spit out the unique ID of any iButton you tap.
#include <OneWire.h>
// This is the pin with the 1-Wire bus on it
OneWire ds(PIN_D0);
// unique serial number read from the key
byte addr[8];
// poll delay (I think 750ms is a magic number for iButton)
int del = 1000;
// Teensy 2.0 has an LED on port 11
int ledpin = 11;
void setup() {
Serial.begin(9600);
pinMode(ledpin, OUTPUT);
}
void loop() {
byte result;
// search looks through all devices on the bus
ds.reset_search();
if(result = !ds.search(addr)) {
// Serial.println("Scanning...");
} else if(OneWire::crc8(addr, 7) != addr[7]) {
Serial.println("Invalid CRC");
delay(del);
return;
} else {
for(byte i=0; i<8; i++) {
Serial.print(addr[i], HEX);
Serial.print(" ");
}
Serial.print("\n");
digitalWrite(ledpin, HIGH);
delay(1000);
digitalWrite(ledpin, LOW);
}
delay(del);
return;
}
That’s the easy part, but the real trick is doing it the other way around and opening a door with the Arduino. This is a little less well-supported, but there’s a library for it. Sure enough, if you run this on one board and impersonate a reader on the other, the reader will report a successful read of an iButton fob.
Buoyed by our success, we hooked up the fake fob to our office door to reveal… nothing. Not even a “wrong fob” beep. After performing the usual println
binary search for debugging, we worked out that when the board attempted to read the eight-bit command the door was sending, its timing was a little bit off and it only got four bits in before losing synchronisation and timing out.
I should explain a little bit about how the 1-Wire protocol works. As the name suggests, all the communication is done by pulling the voltage of a single wire either high or low. A handshake begins with a long (540μs) low “reset” pulse sent by the master device, in this case our office door. The fob should reply “I’m here” by pulling the bus low for about 150μs. If that works, the master may then send an eight-bit command, with bits communicated by high or low voltages in timeslots separated by an 8-ish μs delay. The only command sent by our office door was 0x33, or “READ ROM”, to which the fob is meant to reply with its ID in the same format.
Now, the underlying problem we had was that that the OneWireSlave library we used above used hardcoded timings to read out the bits of the master device’s command. It was getting about four or five timeslots through said command, but then lost track of the rest of the command. The clear fix, then, is to remove the hardcoded timings and instead follow the master, by waiting as long as it takes for the voltage to flip. (The Arduino runs at 16MHz, or about 60ns/clock cycle, so there should be plenty of time to pick up the μs changes.)
And lo and behold, when Kevin implemented this, the door opened!