487 lines
11 KiB
Arduino
487 lines
11 KiB
Arduino
|
/* Magnetic card emulator for ATMEL
|
||
|
*
|
||
|
* This sould work with the original 'MagSpoof' out of the box!
|
||
|
* In this case you should add a second switch, to allow test card and progrmming mode.
|
||
|
*
|
||
|
* (C) 2021 by Andreas Eversberg <jolly@eversberg.eu>
|
||
|
* All Rights Reserved
|
||
|
*
|
||
|
* This program is free software: you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation, either version 3 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
/* to set fused for ATTINY85: (This is default when shipped!)
|
||
|
* avrdude -c usbasp-clone -p t85 -U lfuse:w:0xc0:m -U hfuse:w:0xdf:m -U efuse:w:0xff:m
|
||
|
*/
|
||
|
|
||
|
/* Use a clock speed of 8 MHz. If you change it, also change the fuses!!!!
|
||
|
* The CLKPS bits are set to 0 by software, so that 8 MHz clock is not divided.
|
||
|
*
|
||
|
* Press switch 1 to emulate card, press switch 2 to emulate BSA44 service card.
|
||
|
* The LED will do short flashs, to indicate that power is on.
|
||
|
*
|
||
|
* Hold a switch while powering on, to enter test mode. The LED will light up.
|
||
|
* Press switch 1 to send continuous 0-bits.
|
||
|
* Press switch 2 to send continuous 1-bits.
|
||
|
* Press switch 1 and 2 to send continuos alternating 0- and 1-bits.
|
||
|
* WARNING: In test mode, H-bridge IC becomes quickly very hot. Don't fry it!
|
||
|
*
|
||
|
* To enter programming mode, press both buttons simultaniously.
|
||
|
* The LED will continously blink, to indicate programming mode.
|
||
|
* Press switch 1 to select subscriber number or switch 1 to select security code.
|
||
|
* The LED will then show the digit values by blinking. It can be aborted with any switch.
|
||
|
* After short blinking, a long blink shows that the first digit can be entered.
|
||
|
* Press switch 1 1-10 times to enter the digit. When done, press switch 2 and continue
|
||
|
* with the next digit. When all digits are entered, press switch 2 again.
|
||
|
* A soft flash of the LED (fading in and out) will indicate that the new digits are stored.
|
||
|
* A false input will abort the programming procedure and restart with continuous blinking.
|
||
|
* To abort programming mode, press both buttuns simultaniously.
|
||
|
*/
|
||
|
|
||
|
#define F_CPU 8000000 // Oscillator frequency
|
||
|
|
||
|
extern "C"
|
||
|
{
|
||
|
#include "iso7811.h"
|
||
|
}
|
||
|
#include <avr/eeprom.h>
|
||
|
|
||
|
#define PORT_ENABLE 3 // PIN 2 -> connect to 1-2EN of L293D and to LED with 1K resistor to ground
|
||
|
#define PORT_COIL1 0 // PIN 5 -> connect to 1A of L293D
|
||
|
#define PORT_COIL2 1 // PIN 6 -> connect to 2A of L293D
|
||
|
#define PORT_SWITCH1 2 // PIN 7 -> connect via switch 1 to ground
|
||
|
#define PORT_SWITCH2 4 // PIN 3 -> connect via switch 2 to ground (optional, leave open when unused)
|
||
|
#define PORT_RESET 5 // PIN 1 -> unused, leave open (Don't disable reset when programming fuses!!!)
|
||
|
|
||
|
/* see main.c for more info */
|
||
|
#define CNETZ_LEAD_IN 12
|
||
|
#define CNETZ_LEAD_OUT 150
|
||
|
|
||
|
#define CLOCK_US 200 // Time to wait for half a bit
|
||
|
|
||
|
#define EEPROM_MAGIC 'c' // not equal to sim emulator, this has a capital 'C'
|
||
|
#define EEPROM_VERSION '0'
|
||
|
|
||
|
#define SWITCH1 (digitalRead(PORT_SWITCH1) == LOW)
|
||
|
#define SWITCH2 (digitalRead(PORT_SWITCH2) == LOW)
|
||
|
|
||
|
static char number[9] = "1234567\0";
|
||
|
static char sicherung[6] = "12345";
|
||
|
static char string[19];
|
||
|
static uint8_t flux = 0;
|
||
|
|
||
|
/* enable H-bridge, but set it to neutral */
|
||
|
void enable_h_bridge(uint8_t enable)
|
||
|
{
|
||
|
/* enable H-bridge and LED */
|
||
|
digitalWrite(PORT_ENABLE, enable);
|
||
|
|
||
|
/* set bridge to neutral */
|
||
|
digitalWrite(PORT_COIL1, LOW);
|
||
|
digitalWrite(PORT_COIL2, LOW);
|
||
|
}
|
||
|
|
||
|
/* send single bit with clock */
|
||
|
void send_bit(uint8_t bit)
|
||
|
{
|
||
|
digitalWrite(PORT_COIL1, flux);
|
||
|
flux ^= 1;
|
||
|
digitalWrite(PORT_COIL2, flux);
|
||
|
delayMicroseconds(CLOCK_US);
|
||
|
|
||
|
if (bit & 1) {
|
||
|
digitalWrite(PORT_COIL1, flux);
|
||
|
flux ^= 1;
|
||
|
digitalWrite(PORT_COIL2, flux);
|
||
|
}
|
||
|
delayMicroseconds(CLOCK_US);
|
||
|
}
|
||
|
|
||
|
/* blink exactly one second to confirm correct clock speed */
|
||
|
void blink_led()
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < 3; i++) {
|
||
|
delay(200);
|
||
|
if (SWITCH1 || SWITCH2)
|
||
|
break;
|
||
|
enable_h_bridge(1);
|
||
|
delay(200);
|
||
|
if (SWITCH1 || SWITCH2)
|
||
|
break;
|
||
|
enable_h_bridge(0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* wait until release of single key, but abort when pressing both keys.
|
||
|
* if 2 is given, wait for release of all keys, don't abort when pressing both keys.
|
||
|
* compensate contact shattering (German: Tastenprellen)
|
||
|
*/
|
||
|
void wait_release(int keys)
|
||
|
{
|
||
|
int i = 0;
|
||
|
while (i < 50) {
|
||
|
delay(1);
|
||
|
if (keys < 2) {
|
||
|
if (SWITCH1 && SWITCH2)
|
||
|
break;
|
||
|
}
|
||
|
if (SWITCH1 || SWITCH2)
|
||
|
i = 0;
|
||
|
else
|
||
|
i++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* test mode */
|
||
|
void test_pattern(void)
|
||
|
{
|
||
|
/* test pattern */
|
||
|
while (42) {
|
||
|
if (!SWITCH1 && !SWITCH2)
|
||
|
enable_h_bridge(1);
|
||
|
if (SWITCH1)
|
||
|
send_bit(0);
|
||
|
if (SWITCH2)
|
||
|
send_bit(1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* read eeprom, if version is correct */
|
||
|
void read_eeprom(void)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
if (eeprom_read_byte(0) == EEPROM_MAGIC
|
||
|
&& eeprom_read_byte(1) == EEPROM_VERSION) {
|
||
|
for (i = 0; i < 8; i++)
|
||
|
number[i] = eeprom_read_byte(i + 2);
|
||
|
number[i] = '\0';
|
||
|
for (i = 0; i < 5; i++)
|
||
|
sicherung[i] = eeprom_read_byte(i + 10);
|
||
|
sicherung[i] = '\0';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* write eeprom, */
|
||
|
void write_eeprom(void)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
eeprom_write_byte(0, EEPROM_MAGIC);
|
||
|
eeprom_write_byte(1, EEPROM_VERSION);
|
||
|
for (i = 0; i < 8; i++)
|
||
|
eeprom_write_byte(i + 2, number[i]);
|
||
|
for (i = 0; i < 5; i++)
|
||
|
eeprom_write_byte(i + 10, sicherung[i]);
|
||
|
|
||
|
/* show soft flash */
|
||
|
for (i = 0; i < 55; i++) {
|
||
|
enable_h_bridge(1);
|
||
|
delay(i/5);
|
||
|
enable_h_bridge(0);
|
||
|
delay(10 - i/5);
|
||
|
}
|
||
|
for (i = 54; i >= 0; i--) {
|
||
|
enable_h_bridge(1);
|
||
|
delay(i/5);
|
||
|
enable_h_bridge(0);
|
||
|
delay(10 - i/5);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void setup() {
|
||
|
/* setup clock speed to 8 MHz */
|
||
|
CLKPR = _BV(CLKPCE);
|
||
|
CLKPR = 0;
|
||
|
|
||
|
/* setup ports */
|
||
|
pinMode(PORT_ENABLE, OUTPUT);
|
||
|
digitalWrite(PORT_ENABLE, LOW);
|
||
|
pinMode(PORT_COIL1, OUTPUT);
|
||
|
digitalWrite(PORT_COIL1, LOW);
|
||
|
pinMode(PORT_COIL2, OUTPUT);
|
||
|
digitalWrite(PORT_COIL2, LOW);
|
||
|
pinMode(PORT_SWITCH1, INPUT_PULLUP);
|
||
|
pinMode(PORT_SWITCH2, INPUT_PULLUP);
|
||
|
|
||
|
/* blink with LED */
|
||
|
blink_led();
|
||
|
|
||
|
/* transmit test pattern */
|
||
|
if (SWITCH1 || SWITCH2) {
|
||
|
wait_release(1);
|
||
|
test_pattern();
|
||
|
}
|
||
|
|
||
|
/* read subscriber data from eeprom */
|
||
|
read_eeprom();
|
||
|
}
|
||
|
|
||
|
/* send card data */
|
||
|
void send_string(const char *string)
|
||
|
{
|
||
|
uint8_t data[CNETZ_LEAD_IN + 21 + CNETZ_LEAD_OUT];
|
||
|
int length, i;
|
||
|
uint8_t digit;
|
||
|
|
||
|
length = encode_track(data, string, CNETZ_LEAD_IN, CNETZ_LEAD_OUT);
|
||
|
|
||
|
/* enable H-bridge and LED */
|
||
|
enable_h_bridge(1);
|
||
|
|
||
|
/* send bits */
|
||
|
for (i = 0; i < length; i++) {
|
||
|
digit = data[i];
|
||
|
send_bit((digit >> 0) & 1);
|
||
|
send_bit((digit >> 1) & 1);
|
||
|
send_bit((digit >> 2) & 1);
|
||
|
send_bit((digit >> 3) & 1);
|
||
|
send_bit((digit >> 4) & 1);
|
||
|
/* abort when pressing both switches */
|
||
|
if (SWITCH1 && SWITCH2)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* disable H-bridge */
|
||
|
enable_h_bridge(0);
|
||
|
}
|
||
|
|
||
|
/* send zeros or ones depending on the key pressed, stop by pressing both keys */
|
||
|
void program_mode(void)
|
||
|
{
|
||
|
uint8_t blink;
|
||
|
uint8_t edit;
|
||
|
char io[9];
|
||
|
int i, b, d;
|
||
|
|
||
|
error:
|
||
|
blink = 0;
|
||
|
edit = 0;
|
||
|
/* flash LED, wait for key press */
|
||
|
while (!edit) {
|
||
|
blink ^= 1;
|
||
|
enable_h_bridge(blink);
|
||
|
for (d = 0; d < 50; d++) {
|
||
|
delay(1);
|
||
|
if (SWITCH1) {
|
||
|
edit = 1;
|
||
|
break;
|
||
|
}
|
||
|
if (SWITCH2) {
|
||
|
edit = 2;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
enable_h_bridge(0);
|
||
|
wait_release(1);
|
||
|
if (SWITCH1 && SWITCH2)
|
||
|
goto done;
|
||
|
|
||
|
/* copy subscriber data to io-buffer */
|
||
|
switch (edit) {
|
||
|
case 1:
|
||
|
for (i = 0; i < 8; i++)
|
||
|
io[i] = number[i];
|
||
|
io[i] = '\0';
|
||
|
break;
|
||
|
case 2:
|
||
|
for (i = 0; i < 5; i++)
|
||
|
io[i] = sicherung[i];
|
||
|
io[i] = '\0';
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* blink the io-buffer data */
|
||
|
for (i = 0; io[i]; i++) {
|
||
|
for (d = 0; d < 1000; d++) {
|
||
|
delay(1);
|
||
|
if (SWITCH1 || SWITCH2)
|
||
|
goto stop_blink;
|
||
|
}
|
||
|
if (io[i] > '0')
|
||
|
blink = io[i] - '0';
|
||
|
else
|
||
|
blink = 10;
|
||
|
for (b = 0; b < blink; b++) {
|
||
|
enable_h_bridge(1);
|
||
|
for (d = 0; d < 100; d++) {
|
||
|
delay(1);
|
||
|
if (SWITCH1 || SWITCH2) {
|
||
|
enable_h_bridge(0);
|
||
|
goto stop_blink;
|
||
|
}
|
||
|
}
|
||
|
enable_h_bridge(0);
|
||
|
for (d = 0; d < 400; d++) {
|
||
|
delay(1);
|
||
|
if (SWITCH1 || SWITCH2)
|
||
|
goto stop_blink;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
for (d = 0; d < 1000; d++) {
|
||
|
delay(1);
|
||
|
if (SWITCH1 && SWITCH2)
|
||
|
goto stop_blink;
|
||
|
}
|
||
|
stop_blink:
|
||
|
wait_release(1);
|
||
|
if (SWITCH1 && SWITCH2)
|
||
|
goto done;
|
||
|
enable_h_bridge(1);
|
||
|
for (d = 0; d < 500; d++) {
|
||
|
delay(1);
|
||
|
if (SWITCH1 && SWITCH2)
|
||
|
goto done;
|
||
|
}
|
||
|
enable_h_bridge(0);
|
||
|
|
||
|
/* key in the data to io-buffer */
|
||
|
i = 0;
|
||
|
b = 0;
|
||
|
while (42) {
|
||
|
if (SWITCH1) {
|
||
|
enable_h_bridge(1);
|
||
|
for (d = 0; d < 100; d++) {
|
||
|
delay(1);
|
||
|
if (SWITCH1 && SWITCH2)
|
||
|
goto done;
|
||
|
}
|
||
|
enable_h_bridge(0);
|
||
|
wait_release(1);
|
||
|
if (SWITCH1 && SWITCH2)
|
||
|
goto done;
|
||
|
b++;
|
||
|
if (b > 10)
|
||
|
goto error;
|
||
|
}
|
||
|
if (SWITCH2) {
|
||
|
wait_release(1);
|
||
|
if (SWITCH1 && SWITCH2)
|
||
|
goto done;
|
||
|
if (b == 0) {
|
||
|
while (i < 9)
|
||
|
io[i++] = '\0';
|
||
|
break;
|
||
|
}
|
||
|
if (b == 10)
|
||
|
b = 0;
|
||
|
io[i++] = '0' + b;
|
||
|
b = 0;
|
||
|
if (i > 8)
|
||
|
goto error;
|
||
|
enable_h_bridge(1);
|
||
|
for (d = 0; d < 500; d++) {
|
||
|
delay(1);
|
||
|
if (SWITCH1 && SWITCH2)
|
||
|
goto done;
|
||
|
}
|
||
|
enable_h_bridge(0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* verify input */
|
||
|
switch (edit) {
|
||
|
case 1:
|
||
|
if (strlen(io) < 7)
|
||
|
goto error;
|
||
|
if (io[0] > '7')
|
||
|
goto error;
|
||
|
if (strlen(io) == 8) {
|
||
|
if ((io[1] - '0') * 10 + io[2] - '0' > 31)
|
||
|
goto error;
|
||
|
if (atoi(io + 3) > 65535)
|
||
|
goto error;
|
||
|
} else {
|
||
|
if (atoi(io + 2) > 65535)
|
||
|
goto error;
|
||
|
}
|
||
|
break;
|
||
|
case 2:
|
||
|
if (strlen(io) > 5)
|
||
|
goto error;
|
||
|
if (!io[0])
|
||
|
goto error;
|
||
|
if (io[0] == '0' && io[1] != '\0')
|
||
|
goto error;
|
||
|
if (atoi(io) > 65535)
|
||
|
goto error;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* copy io-buffer data to subscriber data */
|
||
|
switch (edit) {
|
||
|
case 1:
|
||
|
for (i = 0; i < 8; i++)
|
||
|
number[i] = io[i];
|
||
|
number[i] = '\0';
|
||
|
break;
|
||
|
case 2:
|
||
|
for (i = 0; i < 5; i++)
|
||
|
sicherung[i] = io[i];
|
||
|
sicherung[i] = '\0';
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* write subscriber data to eeprom */
|
||
|
write_eeprom();
|
||
|
|
||
|
done:
|
||
|
enable_h_bridge(0);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
static uint16_t flash = 0;
|
||
|
|
||
|
void loop() {
|
||
|
/* go programming */
|
||
|
if (SWITCH1 && SWITCH2) {
|
||
|
flash = 0;
|
||
|
wait_release(2);
|
||
|
program_mode();
|
||
|
wait_release(2);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* send card */
|
||
|
if (SWITCH1) {
|
||
|
flash = 0;
|
||
|
cnetz_card(string, number, sicherung);
|
||
|
send_string(string);
|
||
|
wait_release(1);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* send service card */
|
||
|
if (SWITCH2) {
|
||
|
flash = 0;
|
||
|
bsa44_service(string);
|
||
|
send_string(string);
|
||
|
wait_release(1);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* slow blink to show that the device is powered on */
|
||
|
delay(1);
|
||
|
flash++;
|
||
|
if (flash == 1980)
|
||
|
enable_h_bridge(1);
|
||
|
if (flash == 2000) {
|
||
|
enable_h_bridge(0);
|
||
|
flash = 0;
|
||
|
}
|
||
|
}
|