New LF-RFID app (#534)
* Hal lfrfid: add read timer pulse and period config fns * New debug application for lfrfid subsystem * New lfrfid: app, fix naming * App lfrfid: assets * Container view module * App ibutton: remove unused header * App lfrfid scenes * App notification, add yield to blocking operations, add speaker volume control * App lfrfid: reading key scene * Assets: placeholder icon * App lfrfid: reworked container view module * App lfrfid: new scenes * App lfrfid: write scene * App lfrfid: write hid * App lfrfid: emulate scene * App lfrfid: save name scene * App lfrfid: add missing file
This commit is contained in:
48
applications/lfrfid/helpers/decoder-analyzer.cpp
Normal file
48
applications/lfrfid/helpers/decoder-analyzer.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
#include "decoder-analyzer.h"
|
||||
#include <furi.h>
|
||||
#include <api-hal.h>
|
||||
|
||||
bool DecoderAnalyzer::read(uint8_t* _data, uint8_t _data_size) {
|
||||
bool result = false;
|
||||
|
||||
if(ready) {
|
||||
result = true;
|
||||
|
||||
for(size_t i = 0; i < data_size; i++) {
|
||||
printf("%lu ", data[i]);
|
||||
if((i + 1) % 8 == 0) printf("\r\n");
|
||||
}
|
||||
printf("\r\n--------\r\n");
|
||||
|
||||
ready = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void DecoderAnalyzer::process_front(bool polarity, uint32_t time) {
|
||||
if(ready) return;
|
||||
|
||||
data[data_index] = time;
|
||||
|
||||
if(data_index < data_size) {
|
||||
data_index++;
|
||||
} else {
|
||||
data_index = 0;
|
||||
ready = true;
|
||||
}
|
||||
}
|
||||
|
||||
DecoderAnalyzer::DecoderAnalyzer() {
|
||||
data = reinterpret_cast<uint32_t*>(calloc(data_size, sizeof(uint32_t)));
|
||||
furi_check(data);
|
||||
data_index = 0;
|
||||
ready = false;
|
||||
}
|
||||
|
||||
DecoderAnalyzer::~DecoderAnalyzer() {
|
||||
free(data);
|
||||
}
|
||||
|
||||
void DecoderAnalyzer::reset_state() {
|
||||
}
|
||||
21
applications/lfrfid/helpers/decoder-analyzer.h
Normal file
21
applications/lfrfid/helpers/decoder-analyzer.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <atomic>
|
||||
|
||||
class DecoderAnalyzer {
|
||||
public:
|
||||
bool read(uint8_t* data, uint8_t data_size);
|
||||
void process_front(bool polarity, uint32_t time);
|
||||
|
||||
DecoderAnalyzer();
|
||||
~DecoderAnalyzer();
|
||||
|
||||
private:
|
||||
void reset_state();
|
||||
|
||||
std::atomic<bool> ready;
|
||||
|
||||
static const uint32_t data_size = 2048;
|
||||
uint32_t data_index = 0;
|
||||
uint32_t* data;
|
||||
};
|
||||
72
applications/lfrfid/helpers/decoder-emmarine.cpp
Normal file
72
applications/lfrfid/helpers/decoder-emmarine.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include "emmarine.h"
|
||||
#include "decoder-emmarine.h"
|
||||
#include <furi.h>
|
||||
#include <api-hal.h>
|
||||
|
||||
constexpr uint32_t clocks_in_us = 64;
|
||||
constexpr uint32_t short_time = 255 * clocks_in_us;
|
||||
constexpr uint32_t long_time = 510 * clocks_in_us;
|
||||
constexpr uint32_t jitter_time = 100 * clocks_in_us;
|
||||
|
||||
constexpr uint32_t short_time_low = short_time - jitter_time;
|
||||
constexpr uint32_t short_time_high = short_time + jitter_time;
|
||||
constexpr uint32_t long_time_low = long_time - jitter_time;
|
||||
constexpr uint32_t long_time_high = long_time + jitter_time;
|
||||
|
||||
void DecoderEMMarine::reset_state() {
|
||||
ready = false;
|
||||
readed_data = 0;
|
||||
manchester_advance(
|
||||
manchester_saved_state, ManchesterEventReset, &manchester_saved_state, nullptr);
|
||||
}
|
||||
|
||||
bool DecoderEMMarine::read(uint8_t* data, uint8_t data_size) {
|
||||
bool result = false;
|
||||
|
||||
if(ready) {
|
||||
result = true;
|
||||
em_marine.decode(
|
||||
reinterpret_cast<const uint8_t*>(&readed_data), sizeof(uint64_t), data, data_size);
|
||||
ready = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void DecoderEMMarine::process_front(bool polarity, uint32_t time) {
|
||||
if(ready) return;
|
||||
if(time < short_time_low) return;
|
||||
|
||||
ManchesterEvent event = ManchesterEventReset;
|
||||
|
||||
if(time > short_time_low && time < short_time_high) {
|
||||
if(polarity) {
|
||||
event = ManchesterEventShortHigh;
|
||||
} else {
|
||||
event = ManchesterEventShortLow;
|
||||
}
|
||||
} else if(time > long_time_low && time < long_time_high) {
|
||||
if(polarity) {
|
||||
event = ManchesterEventLongHigh;
|
||||
} else {
|
||||
event = ManchesterEventLongLow;
|
||||
}
|
||||
}
|
||||
|
||||
if(event != ManchesterEventReset) {
|
||||
bool data;
|
||||
bool data_ok =
|
||||
manchester_advance(manchester_saved_state, event, &manchester_saved_state, &data);
|
||||
|
||||
if(data_ok) {
|
||||
readed_data = (readed_data << 1) | data;
|
||||
|
||||
ready = em_marine.can_be_decoded(
|
||||
reinterpret_cast<const uint8_t*>(&readed_data), sizeof(uint64_t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DecoderEMMarine::DecoderEMMarine() {
|
||||
reset_state();
|
||||
}
|
||||
21
applications/lfrfid/helpers/decoder-emmarine.h
Normal file
21
applications/lfrfid/helpers/decoder-emmarine.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <atomic>
|
||||
#include "manchester-decoder.h"
|
||||
#include "protocols/protocol-emmarin.h"
|
||||
class DecoderEMMarine {
|
||||
public:
|
||||
bool read(uint8_t* data, uint8_t data_size);
|
||||
void process_front(bool polarity, uint32_t time);
|
||||
|
||||
DecoderEMMarine();
|
||||
|
||||
private:
|
||||
void reset_state();
|
||||
|
||||
uint64_t readed_data = 0;
|
||||
std::atomic<bool> ready;
|
||||
|
||||
ManchesterState manchester_saved_state;
|
||||
ProtocolEMMarin em_marine;
|
||||
};
|
||||
98
applications/lfrfid/helpers/decoder-hid26.cpp
Normal file
98
applications/lfrfid/helpers/decoder-hid26.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
#include "decoder-hid26.h"
|
||||
#include <api-hal.h>
|
||||
|
||||
constexpr uint32_t clocks_in_us = 64;
|
||||
|
||||
constexpr uint32_t jitter_time_us = 20;
|
||||
constexpr uint32_t min_time_us = 64;
|
||||
constexpr uint32_t max_time_us = 80;
|
||||
|
||||
constexpr uint32_t min_time = (min_time_us - jitter_time_us) * clocks_in_us;
|
||||
constexpr uint32_t mid_time = ((max_time_us - min_time_us) / 2 + min_time_us) * clocks_in_us;
|
||||
constexpr uint32_t max_time = (max_time_us + jitter_time_us) * clocks_in_us;
|
||||
|
||||
bool DecoderHID26::read(uint8_t* data, uint8_t data_size) {
|
||||
bool result = false;
|
||||
furi_assert(data_size >= 3);
|
||||
|
||||
if(ready) {
|
||||
result = true;
|
||||
hid.decode(
|
||||
reinterpret_cast<const uint8_t*>(&stored_data), sizeof(uint32_t) * 3, data, data_size);
|
||||
ready = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void DecoderHID26::process_front(bool polarity, uint32_t time) {
|
||||
if(ready) return;
|
||||
|
||||
if(polarity == true) {
|
||||
last_pulse_time = time;
|
||||
} else {
|
||||
last_pulse_time += time;
|
||||
|
||||
if(last_pulse_time > min_time && last_pulse_time < max_time) {
|
||||
bool pulse;
|
||||
|
||||
if(last_pulse_time < mid_time) {
|
||||
// 6 pulses
|
||||
pulse = false;
|
||||
} else {
|
||||
// 5 pulses
|
||||
pulse = true;
|
||||
}
|
||||
|
||||
if(last_pulse == pulse) {
|
||||
pulse_count++;
|
||||
|
||||
if(pulse) {
|
||||
if(pulse_count > 4) {
|
||||
pulse_count = 0;
|
||||
store_data(1);
|
||||
}
|
||||
} else {
|
||||
if(pulse_count > 5) {
|
||||
pulse_count = 0;
|
||||
store_data(0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(last_pulse) {
|
||||
if(pulse_count > 2) {
|
||||
store_data(1);
|
||||
}
|
||||
} else {
|
||||
if(pulse_count > 3) {
|
||||
store_data(0);
|
||||
}
|
||||
}
|
||||
|
||||
pulse_count = 0;
|
||||
last_pulse = pulse;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DecoderHID26::DecoderHID26() {
|
||||
reset_state();
|
||||
}
|
||||
|
||||
void DecoderHID26::store_data(bool data) {
|
||||
stored_data[0] = (stored_data[0] << 1) | ((stored_data[1] >> 31) & 1);
|
||||
stored_data[1] = (stored_data[1] << 1) | ((stored_data[2] >> 31) & 1);
|
||||
stored_data[2] = (stored_data[2] << 1) | data;
|
||||
|
||||
if(hid.can_be_decoded(reinterpret_cast<const uint8_t*>(&stored_data), sizeof(uint32_t) * 3)) {
|
||||
ready = true;
|
||||
}
|
||||
}
|
||||
|
||||
void DecoderHID26::reset_state() {
|
||||
last_pulse = false;
|
||||
pulse_count = 0;
|
||||
ready = false;
|
||||
last_pulse_time = 0;
|
||||
}
|
||||
24
applications/lfrfid/helpers/decoder-hid26.h
Normal file
24
applications/lfrfid/helpers/decoder-hid26.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <atomic>
|
||||
#include "protocols/protocol-hid-h10301.h"
|
||||
|
||||
class DecoderHID26 {
|
||||
public:
|
||||
bool read(uint8_t* data, uint8_t data_size);
|
||||
void process_front(bool polarity, uint32_t time);
|
||||
DecoderHID26();
|
||||
|
||||
private:
|
||||
uint32_t last_pulse_time = 0;
|
||||
bool last_pulse;
|
||||
uint8_t pulse_count;
|
||||
|
||||
uint32_t stored_data[3] = {0, 0, 0};
|
||||
void store_data(bool data);
|
||||
|
||||
std::atomic<bool> ready;
|
||||
|
||||
void reset_state();
|
||||
ProtocolHID10301 hid;
|
||||
};
|
||||
170
applications/lfrfid/helpers/decoder-indala.cpp
Normal file
170
applications/lfrfid/helpers/decoder-indala.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
#include "decoder-indala.h"
|
||||
#include <api-hal.h>
|
||||
|
||||
constexpr uint32_t clocks_in_us = 64;
|
||||
|
||||
constexpr uint32_t min_time_us = 25 * clocks_in_us;
|
||||
constexpr uint32_t mid_time_us = 45 * clocks_in_us;
|
||||
constexpr uint32_t max_time_us = 90 * clocks_in_us;
|
||||
|
||||
bool DecoderIndala::read(uint8_t* data, uint8_t data_size) {
|
||||
bool result = false;
|
||||
|
||||
if(ready) {
|
||||
result = true;
|
||||
printf("IND %02X %02X %02X\r\n", facility, (uint8_t)(number >> 8), (uint8_t)number);
|
||||
ready = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void DecoderIndala::process_front(bool polarity, uint32_t time) {
|
||||
if(ready) return;
|
||||
|
||||
if(polarity == false) {
|
||||
last_pulse_time = time;
|
||||
} else {
|
||||
last_pulse_time += time;
|
||||
pulse_count++;
|
||||
|
||||
if(last_pulse_time > min_time_us && last_pulse_time < max_time_us) {
|
||||
if(last_pulse_time > mid_time_us) {
|
||||
bool last_data = !(readed_data & 1);
|
||||
pulse_count = 0;
|
||||
readed_data = (readed_data << 1) | last_data;
|
||||
verify();
|
||||
} else if((pulse_count % 16) == 0) {
|
||||
bool last_data = readed_data & 1;
|
||||
pulse_count = 0;
|
||||
readed_data = (readed_data << 1) | last_data;
|
||||
verify();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DecoderIndala::DecoderIndala() {
|
||||
}
|
||||
|
||||
void DecoderIndala::reset_state() {
|
||||
}
|
||||
|
||||
void DecoderIndala::verify() {
|
||||
// verify inverse
|
||||
readed_data = ~readed_data;
|
||||
verify_inner();
|
||||
|
||||
// verify normal
|
||||
readed_data = ~readed_data;
|
||||
verify_inner();
|
||||
}
|
||||
|
||||
typedef union {
|
||||
uint64_t raw;
|
||||
struct __attribute__((packed)) {
|
||||
uint8_t static0 : 3;
|
||||
uint8_t checksum : 2;
|
||||
uint8_t static1 : 2;
|
||||
uint8_t y14 : 1;
|
||||
|
||||
uint8_t x8 : 1;
|
||||
uint8_t x1 : 1;
|
||||
uint8_t y13 : 1;
|
||||
uint8_t static2 : 1;
|
||||
uint8_t y12 : 1;
|
||||
uint8_t x6 : 1;
|
||||
uint8_t y5 : 1;
|
||||
uint8_t y8 : 1;
|
||||
|
||||
uint8_t y15 : 1;
|
||||
uint8_t x2 : 1;
|
||||
uint8_t x5 : 1;
|
||||
uint8_t x4 : 1;
|
||||
uint8_t y9 : 1;
|
||||
uint8_t y2 : 1;
|
||||
uint8_t x3 : 1;
|
||||
uint8_t y3 : 1;
|
||||
|
||||
uint8_t y1 : 1;
|
||||
uint8_t y16 : 1;
|
||||
uint8_t y4 : 1;
|
||||
uint8_t x7 : 1;
|
||||
uint8_t p2 : 1;
|
||||
uint8_t y11 : 1;
|
||||
uint8_t y6 : 1;
|
||||
uint8_t y7 : 1;
|
||||
|
||||
uint8_t p1 : 1;
|
||||
uint8_t y10 : 1;
|
||||
uint32_t preamble : 30;
|
||||
};
|
||||
} IndalaFormat;
|
||||
|
||||
void DecoderIndala::verify_inner() {
|
||||
IndalaFormat id;
|
||||
id.raw = readed_data;
|
||||
|
||||
// preamble
|
||||
//if((data >> 34) != 0b000000000000000000000000000001) return;
|
||||
if(id.preamble != 1) return;
|
||||
|
||||
// static data bits
|
||||
//if((data & 0b100001100111) != 0b101) return;
|
||||
if(id.static2 != 0 && id.static1 != 0 && id.static0 != 0b101) return;
|
||||
|
||||
// Indala checksum
|
||||
uint8_t sum_to_check = id.y2 + id.y4 + id.y7 + id.y8 + id.y10 + id.y11 + id.y14 + id.y16;
|
||||
|
||||
if(sum_to_check % 2 == 0) {
|
||||
if(id.checksum != 0b10) return;
|
||||
} else {
|
||||
if(id.checksum != 0b01) return;
|
||||
}
|
||||
|
||||
// read facility number
|
||||
facility = (id.x1 << 7) + (id.x2 << 6) + (id.x3 << 5) + (id.x4 << 4) + (id.x5 << 3) +
|
||||
(id.x6 << 2) + (id.x7 << 1) + (id.x8 << 0);
|
||||
|
||||
// read serial number
|
||||
number = (id.y1 << 15) + (id.y2 << 14) + (id.y3 << 13) + (id.y4 << 12) + (id.y5 << 11) +
|
||||
(id.y6 << 10) + (id.y7 << 9) + (id.y8 << 8) + (id.y9 << 7) + (id.y10 << 6) +
|
||||
(id.y11 << 5) + (id.y12 << 4) + (id.y13 << 3) + (id.y14 << 2) + (id.y15 << 1) +
|
||||
(id.y16 << 0);
|
||||
|
||||
// Wiegand checksum left
|
||||
sum_to_check = 0;
|
||||
for(int8_t i = 0; i < 8; i--) {
|
||||
if((facility >> i) & 1) {
|
||||
sum_to_check += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for(int8_t i = 0; i < 4; i--) {
|
||||
if((number >> i) & 1) {
|
||||
sum_to_check += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(id.p1) {
|
||||
sum_to_check += 1;
|
||||
}
|
||||
|
||||
if((sum_to_check % 2) == 1) return;
|
||||
|
||||
// Wiegand checksum right
|
||||
sum_to_check = 0;
|
||||
for(int8_t i = 0; i < 12; i--) {
|
||||
if((number >> (i + 4)) & 1) {
|
||||
sum_to_check += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(id.p2) {
|
||||
sum_to_check += 1;
|
||||
}
|
||||
|
||||
if((sum_to_check % 2) != 1) return;
|
||||
|
||||
ready = true;
|
||||
}
|
||||
28
applications/lfrfid/helpers/decoder-indala.h
Normal file
28
applications/lfrfid/helpers/decoder-indala.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include <atomic>
|
||||
|
||||
class DecoderIndala {
|
||||
public:
|
||||
bool read(uint8_t* data, uint8_t data_size);
|
||||
void process_front(bool polarity, uint32_t time);
|
||||
|
||||
DecoderIndala();
|
||||
|
||||
private:
|
||||
void reset_state();
|
||||
|
||||
void verify();
|
||||
void verify_inner();
|
||||
|
||||
uint32_t last_pulse_time = 0;
|
||||
uint32_t pulse_count = 0;
|
||||
uint32_t overall_pulse_count = 0;
|
||||
|
||||
uint64_t readed_data = 0;
|
||||
|
||||
std::atomic<bool> ready;
|
||||
uint8_t facility = 0;
|
||||
uint16_t number = 0;
|
||||
};
|
||||
15
applications/lfrfid/helpers/emmarine.h
Normal file
15
applications/lfrfid/helpers/emmarine.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
#define EM_HEADER_POS 55
|
||||
#define EM_HEADER_MASK (0x1FFLLU << EM_HEADER_POS)
|
||||
|
||||
#define EM_FIRST_ROW_POS 50
|
||||
#define EM_ROW_COUNT 10
|
||||
|
||||
#define EM_COLUMN_POS 4
|
||||
#define EM_STOP_POS 0
|
||||
#define EM_STOP_MASK (0x1LLU << EM_STOP_POS)
|
||||
|
||||
#define EM_HEADER_AND_STOP_MASK (EM_HEADER_MASK | EM_STOP_MASK)
|
||||
#define EM_HEADER_AND_STOP_DATA (EM_HEADER_MASK)
|
||||
24
applications/lfrfid/helpers/encoder-emmarine.cpp
Normal file
24
applications/lfrfid/helpers/encoder-emmarine.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#include "encoder-emmarine.h"
|
||||
#include "protocols/protocol-emmarin.h"
|
||||
#include <furi.h>
|
||||
|
||||
void EncoderEM::init(const uint8_t* data, const uint8_t data_size) {
|
||||
ProtocolEMMarin em_marin;
|
||||
em_marin.encode(data, data_size, reinterpret_cast<uint8_t*>(&card_data), sizeof(uint64_t));
|
||||
|
||||
card_data_index = 0;
|
||||
}
|
||||
|
||||
// data transmitted as manchester encoding
|
||||
// 0 - high2low
|
||||
// 1 - low2high
|
||||
void EncoderEM::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) {
|
||||
*period = clocks_per_bit;
|
||||
*pulse = clocks_per_bit / 2;
|
||||
*polarity = (card_data >> (63 - card_data_index)) & 1;
|
||||
|
||||
card_data_index++;
|
||||
if(card_data_index >= 64) {
|
||||
card_data_index = 0;
|
||||
}
|
||||
}
|
||||
22
applications/lfrfid/helpers/encoder-emmarine.h
Normal file
22
applications/lfrfid/helpers/encoder-emmarine.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include "encoder-generic.h"
|
||||
|
||||
class EncoderEM : public EncoderGeneric {
|
||||
public:
|
||||
/**
|
||||
* @brief init data to emulate
|
||||
*
|
||||
* @param data 1 byte FC, next 4 byte SN
|
||||
* @param data_size must be 5
|
||||
*/
|
||||
void init(const uint8_t* data, const uint8_t data_size) final;
|
||||
|
||||
void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final;
|
||||
|
||||
private:
|
||||
// clock pulses per bit
|
||||
static const uint8_t clocks_per_bit = 64;
|
||||
|
||||
uint64_t card_data;
|
||||
uint8_t card_data_index;
|
||||
};
|
||||
27
applications/lfrfid/helpers/encoder-generic.h
Normal file
27
applications/lfrfid/helpers/encoder-generic.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
class EncoderGeneric {
|
||||
public:
|
||||
/**
|
||||
* @brief init encoder
|
||||
*
|
||||
* @param data data array
|
||||
* @param data_size data array size
|
||||
*/
|
||||
virtual void init(const uint8_t* data, const uint8_t data_size) = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the next timer pulse
|
||||
*
|
||||
* @param polarity pulse polarity true = high2low, false = low2high
|
||||
* @param period overall period time in timer clicks
|
||||
* @param pulse pulse time in timer clicks
|
||||
*/
|
||||
virtual void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) = 0;
|
||||
|
||||
virtual ~EncoderGeneric(){};
|
||||
|
||||
private:
|
||||
};
|
||||
62
applications/lfrfid/helpers/encoder-hid-h10301.cpp
Normal file
62
applications/lfrfid/helpers/encoder-hid-h10301.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#include "encoder-hid-h10301.h"
|
||||
#include "protocols/protocol-hid-h10301.h"
|
||||
#include <furi.h>
|
||||
|
||||
void EncoderHID_H10301::init(const uint8_t* data, const uint8_t data_size) {
|
||||
ProtocolHID10301 hid;
|
||||
hid.encode(data, data_size, reinterpret_cast<uint8_t*>(&card_data), sizeof(card_data) * 3);
|
||||
|
||||
card_data_index = 0;
|
||||
bit_index = 0;
|
||||
}
|
||||
|
||||
void EncoderHID_H10301::write_bit(bool bit, uint8_t position) {
|
||||
write_raw_bit(bit, position + 0);
|
||||
write_raw_bit(!bit, position + 1);
|
||||
}
|
||||
|
||||
void EncoderHID_H10301::write_raw_bit(bool bit, uint8_t position) {
|
||||
if(bit) {
|
||||
card_data[position / 32] |= 1UL << (31 - (position % 32));
|
||||
} else {
|
||||
card_data[position / 32] &= ~(1UL << (31 - (position % 32)));
|
||||
}
|
||||
}
|
||||
|
||||
void EncoderHID_H10301::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) {
|
||||
// hid 0 is 6 cycles by 8 clocks
|
||||
const uint8_t hid_0_period = 8;
|
||||
const uint8_t hid_0_count = 6;
|
||||
// hid 1 is 5 cycles by 10 clocks
|
||||
const uint8_t hid_1_period = 10;
|
||||
const uint8_t hid_1_count = 5;
|
||||
|
||||
bool bit = (card_data[card_data_index / 32] >> (31 - (card_data_index % 32))) & 1;
|
||||
|
||||
*polarity = true;
|
||||
if(bit) {
|
||||
*period = hid_1_period;
|
||||
*pulse = hid_1_period / 2;
|
||||
|
||||
bit_index++;
|
||||
if(bit_index >= hid_1_count) {
|
||||
bit_index = 0;
|
||||
card_data_index++;
|
||||
if(card_data_index >= (32 * card_data_max)) {
|
||||
card_data_index = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
*period = hid_0_period;
|
||||
*pulse = hid_0_period / 2;
|
||||
|
||||
bit_index++;
|
||||
if(bit_index >= hid_0_count) {
|
||||
bit_index = 0;
|
||||
card_data_index++;
|
||||
if(card_data_index >= (32 * card_data_max)) {
|
||||
card_data_index = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
24
applications/lfrfid/helpers/encoder-hid-h10301.h
Normal file
24
applications/lfrfid/helpers/encoder-hid-h10301.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include "encoder-generic.h"
|
||||
|
||||
class EncoderHID_H10301 : public EncoderGeneric {
|
||||
public:
|
||||
/**
|
||||
* @brief init data to emulate
|
||||
*
|
||||
* @param data 1 byte FC, next 2 byte SN
|
||||
* @param data_size must be 3
|
||||
*/
|
||||
void init(const uint8_t* data, const uint8_t data_size) final;
|
||||
|
||||
void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final;
|
||||
|
||||
private:
|
||||
static const uint8_t card_data_max = 3;
|
||||
uint32_t card_data[card_data_max];
|
||||
uint8_t card_data_index;
|
||||
uint8_t bit_index;
|
||||
|
||||
void write_bit(bool bit, uint8_t position);
|
||||
void write_raw_bit(bool bit, uint8_t position);
|
||||
};
|
||||
36
applications/lfrfid/helpers/encoder-indala-40134.cpp
Normal file
36
applications/lfrfid/helpers/encoder-indala-40134.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "encoder-indala-40134.h"
|
||||
#include "protocols/protocol-indala-40134.h"
|
||||
#include <furi.h>
|
||||
|
||||
void EncoderIndala_40134::init(const uint8_t* data, const uint8_t data_size) {
|
||||
ProtocolIndala40134 indala;
|
||||
indala.encode(data, data_size, reinterpret_cast<uint8_t*>(&card_data), sizeof(card_data));
|
||||
|
||||
last_bit = card_data & 1;
|
||||
card_data_index = 0;
|
||||
current_polarity = true;
|
||||
}
|
||||
|
||||
void EncoderIndala_40134::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) {
|
||||
*period = 2;
|
||||
*pulse = 1;
|
||||
*polarity = current_polarity;
|
||||
|
||||
bit_clock_index++;
|
||||
if(bit_clock_index >= clock_per_bit) {
|
||||
bit_clock_index = 0;
|
||||
|
||||
bool current_bit = (card_data >> (63 - card_data_index)) & 1;
|
||||
|
||||
if(current_bit != last_bit) {
|
||||
current_polarity = !current_polarity;
|
||||
}
|
||||
|
||||
last_bit = current_bit;
|
||||
|
||||
card_data_index++;
|
||||
if(card_data_index >= 64) {
|
||||
card_data_index = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
23
applications/lfrfid/helpers/encoder-indala-40134.h
Normal file
23
applications/lfrfid/helpers/encoder-indala-40134.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include "encoder-generic.h"
|
||||
|
||||
class EncoderIndala_40134 : public EncoderGeneric {
|
||||
public:
|
||||
/**
|
||||
* @brief init data to emulate
|
||||
*
|
||||
* @param data indala raw data
|
||||
* @param data_size must be 5
|
||||
*/
|
||||
void init(const uint8_t* data, const uint8_t data_size) final;
|
||||
|
||||
void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final;
|
||||
|
||||
private:
|
||||
uint64_t card_data;
|
||||
uint8_t card_data_index;
|
||||
uint8_t bit_clock_index;
|
||||
bool last_bit;
|
||||
bool current_polarity;
|
||||
static const uint8_t clock_per_bit = 16;
|
||||
};
|
||||
33
applications/lfrfid/helpers/key-info.cpp
Normal file
33
applications/lfrfid/helpers/key-info.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "key-info.h"
|
||||
|
||||
const char* lfrfid_key_get_type_string(LfrfidKeyType type) {
|
||||
switch(type) {
|
||||
case LfrfidKeyType::KeyEM4100:
|
||||
return "EM4100";
|
||||
break;
|
||||
case LfrfidKeyType::KeyH10301:
|
||||
return "H10301";
|
||||
break;
|
||||
case LfrfidKeyType::KeyI40134:
|
||||
return "I40134";
|
||||
break;
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
uint8_t lfrfid_key_get_type_data_count(LfrfidKeyType type) {
|
||||
switch(type) {
|
||||
case LfrfidKeyType::KeyEM4100:
|
||||
return 5;
|
||||
break;
|
||||
case LfrfidKeyType::KeyH10301:
|
||||
return 3;
|
||||
break;
|
||||
case LfrfidKeyType::KeyI40134:
|
||||
return 3;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
14
applications/lfrfid/helpers/key-info.h
Normal file
14
applications/lfrfid/helpers/key-info.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
static const uint8_t LFRFID_KEY_SIZE = 8;
|
||||
static const uint8_t LFRFID_KEY_NAME_SIZE = 22;
|
||||
|
||||
enum class LfrfidKeyType : uint8_t {
|
||||
KeyEM4100,
|
||||
KeyH10301,
|
||||
KeyI40134,
|
||||
};
|
||||
|
||||
const char* lfrfid_key_get_type_string(LfrfidKeyType type);
|
||||
uint8_t lfrfid_key_get_type_data_count(LfrfidKeyType type);
|
||||
34
applications/lfrfid/helpers/manchester-decoder.c
Normal file
34
applications/lfrfid/helpers/manchester-decoder.c
Normal file
@@ -0,0 +1,34 @@
|
||||
#include "manchester-decoder.h"
|
||||
#include <stdint.h>
|
||||
|
||||
static const uint8_t transitions[] = {0b00000001, 0b10010001, 0b10011011, 0b11111011};
|
||||
static const ManchesterState manchester_reset_state = ManchesterStateMid1;
|
||||
|
||||
bool manchester_advance(
|
||||
ManchesterState state,
|
||||
ManchesterEvent event,
|
||||
ManchesterState* next_state,
|
||||
bool* data) {
|
||||
bool result = false;
|
||||
ManchesterState new_state;
|
||||
|
||||
if(event == ManchesterEventReset) {
|
||||
new_state = manchester_reset_state;
|
||||
} else {
|
||||
new_state = transitions[state] >> event & 0x3;
|
||||
if(new_state == state) {
|
||||
new_state = manchester_reset_state;
|
||||
} else {
|
||||
if(new_state == ManchesterStateMid0) {
|
||||
*data = false;
|
||||
result = true;
|
||||
} else if(new_state == ManchesterStateMid1) {
|
||||
*data = true;
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*next_state = new_state;
|
||||
return result;
|
||||
}
|
||||
31
applications/lfrfid/helpers/manchester-decoder.h
Normal file
31
applications/lfrfid/helpers/manchester-decoder.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
ManchesterEventShortLow = 0,
|
||||
ManchesterEventShortHigh = 2,
|
||||
ManchesterEventLongLow = 4,
|
||||
ManchesterEventLongHigh = 6,
|
||||
ManchesterEventReset = 8
|
||||
} ManchesterEvent;
|
||||
|
||||
typedef enum {
|
||||
ManchesterStateStart1 = 0,
|
||||
ManchesterStateMid1 = 1,
|
||||
ManchesterStateMid0 = 2,
|
||||
ManchesterStateStart0 = 3
|
||||
} ManchesterState;
|
||||
|
||||
bool manchester_advance(
|
||||
ManchesterState state,
|
||||
ManchesterEvent event,
|
||||
ManchesterState* next_state,
|
||||
bool* data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
150
applications/lfrfid/helpers/protocols/protocol-emmarin.cpp
Normal file
150
applications/lfrfid/helpers/protocols/protocol-emmarin.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
#include "protocol-emmarin.h"
|
||||
#include <furi.h>
|
||||
|
||||
#define EM_HEADER_POS 55
|
||||
#define EM_HEADER_MASK (0x1FFLLU << EM_HEADER_POS)
|
||||
|
||||
#define EM_FIRST_ROW_POS 50
|
||||
|
||||
#define EM_ROW_COUNT 10
|
||||
#define EM_COLUMN_COUNT 4
|
||||
#define EM_BITS_PER_ROW_COUNT (EM_COLUMN_COUNT + 1)
|
||||
|
||||
#define EM_COLUMN_POS 4
|
||||
#define EM_STOP_POS 0
|
||||
#define EM_STOP_MASK (0x1LLU << EM_STOP_POS)
|
||||
|
||||
#define EM_HEADER_AND_STOP_MASK (EM_HEADER_MASK | EM_STOP_MASK)
|
||||
#define EM_HEADER_AND_STOP_DATA (EM_HEADER_MASK)
|
||||
|
||||
typedef uint64_t EMMarinCardData;
|
||||
|
||||
void write_nibble(bool low_nibble, uint8_t data, EMMarinCardData* card_data) {
|
||||
uint8_t parity_sum = 0;
|
||||
uint8_t start = 0;
|
||||
if(!low_nibble) start = 4;
|
||||
|
||||
for(int8_t i = (start + 3); i >= start; i--) {
|
||||
parity_sum += (data >> i) & 1;
|
||||
*card_data = (*card_data << 1) | ((data >> i) & 1);
|
||||
}
|
||||
|
||||
*card_data = (*card_data << 1) | ((parity_sum % 2) & 1);
|
||||
}
|
||||
|
||||
uint8_t ProtocolEMMarin::get_encoded_data_size() {
|
||||
return sizeof(EMMarinCardData);
|
||||
}
|
||||
|
||||
uint8_t ProtocolEMMarin::get_decoded_data_size() {
|
||||
return 5;
|
||||
}
|
||||
|
||||
void ProtocolEMMarin::encode(
|
||||
const uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size,
|
||||
uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size) {
|
||||
furi_check(decoded_data_size >= get_decoded_data_size());
|
||||
furi_check(encoded_data_size >= get_encoded_data_size());
|
||||
|
||||
EMMarinCardData card_data;
|
||||
|
||||
// header
|
||||
card_data = 0b111111111;
|
||||
|
||||
// data
|
||||
for(uint8_t i = 0; i < get_decoded_data_size(); i++) {
|
||||
write_nibble(false, decoded_data[i], &card_data);
|
||||
write_nibble(true, decoded_data[i], &card_data);
|
||||
}
|
||||
|
||||
// column parity and stop bit
|
||||
uint8_t parity_sum;
|
||||
|
||||
for(uint8_t c = 0; c < EM_COLUMN_COUNT; c++) {
|
||||
parity_sum = 0;
|
||||
for(uint8_t i = 1; i <= EM_ROW_COUNT; i++) {
|
||||
uint8_t parity_bit = (card_data >> (i * EM_BITS_PER_ROW_COUNT - 1)) & 1;
|
||||
parity_sum += parity_bit;
|
||||
}
|
||||
card_data = (card_data << 1) | ((parity_sum % 2) & 1);
|
||||
}
|
||||
|
||||
// stop bit
|
||||
card_data = (card_data << 1) | 0;
|
||||
|
||||
memcpy(encoded_data, &card_data, get_encoded_data_size());
|
||||
}
|
||||
|
||||
void ProtocolEMMarin::decode(
|
||||
const uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size,
|
||||
uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size) {
|
||||
furi_check(decoded_data_size >= get_decoded_data_size());
|
||||
furi_check(encoded_data_size >= get_encoded_data_size());
|
||||
|
||||
uint8_t decoded_data_index = 0;
|
||||
EMMarinCardData card_data = *(reinterpret_cast<const EMMarinCardData*>(encoded_data));
|
||||
|
||||
// clean result
|
||||
memset(decoded_data, 0, decoded_data_size);
|
||||
|
||||
// header
|
||||
for(uint8_t i = 0; i < 9; i++) {
|
||||
card_data = card_data << 1;
|
||||
}
|
||||
|
||||
// nibbles
|
||||
uint8_t value = 0;
|
||||
for(uint8_t r = 0; r < EM_ROW_COUNT; r++) {
|
||||
uint8_t nibble = 0;
|
||||
for(uint8_t i = 0; i < 5; i++) {
|
||||
if(i < 4) nibble = (nibble << 1) | (card_data & (1LLU << 63) ? 1 : 0);
|
||||
card_data = card_data << 1;
|
||||
}
|
||||
value = (value << 4) | nibble;
|
||||
if(r % 2) {
|
||||
decoded_data[decoded_data_index] |= value;
|
||||
decoded_data_index++;
|
||||
value = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ProtocolEMMarin::can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) {
|
||||
furi_check(encoded_data_size >= get_encoded_data_size());
|
||||
const EMMarinCardData* card_data = reinterpret_cast<const EMMarinCardData*>(encoded_data);
|
||||
|
||||
// check header and stop bit
|
||||
if((*card_data & EM_HEADER_AND_STOP_MASK) != EM_HEADER_AND_STOP_DATA) return false;
|
||||
|
||||
// check row parity
|
||||
for(uint8_t i = 0; i < EM_ROW_COUNT; i++) {
|
||||
uint8_t parity_sum = 0;
|
||||
|
||||
for(uint8_t j = 0; j < EM_BITS_PER_ROW_COUNT; j++) {
|
||||
parity_sum += (*card_data >> (EM_FIRST_ROW_POS - i * EM_BITS_PER_ROW_COUNT + j)) & 1;
|
||||
}
|
||||
|
||||
if((parity_sum % 2)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// check columns parity
|
||||
for(uint8_t i = 0; i < EM_COLUMN_COUNT; i++) {
|
||||
uint8_t parity_sum = 0;
|
||||
|
||||
for(uint8_t j = 0; j < EM_ROW_COUNT + 1; j++) {
|
||||
parity_sum += (*card_data >> (EM_COLUMN_POS - i + j * EM_BITS_PER_ROW_COUNT)) & 1;
|
||||
}
|
||||
|
||||
if((parity_sum % 2)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
22
applications/lfrfid/helpers/protocols/protocol-emmarin.h
Normal file
22
applications/lfrfid/helpers/protocols/protocol-emmarin.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include "protocol-generic.h"
|
||||
|
||||
class ProtocolEMMarin : public ProtocolGeneric {
|
||||
public:
|
||||
uint8_t get_encoded_data_size() final;
|
||||
uint8_t get_decoded_data_size() final;
|
||||
|
||||
void encode(
|
||||
const uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size,
|
||||
uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size) final;
|
||||
|
||||
void decode(
|
||||
const uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size,
|
||||
uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size) final;
|
||||
|
||||
bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final;
|
||||
};
|
||||
60
applications/lfrfid/helpers/protocols/protocol-generic.h
Normal file
60
applications/lfrfid/helpers/protocols/protocol-generic.h
Normal file
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
#include "stdint.h"
|
||||
#include "stdbool.h"
|
||||
|
||||
class ProtocolGeneric {
|
||||
public:
|
||||
/**
|
||||
* @brief Get the encoded data size
|
||||
*
|
||||
* @return uint8_t size of encoded data in bytes
|
||||
*/
|
||||
virtual uint8_t get_encoded_data_size() = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the decoded data size
|
||||
*
|
||||
* @return uint8_t size of decoded data in bytes
|
||||
*/
|
||||
virtual uint8_t get_decoded_data_size() = 0;
|
||||
|
||||
/**
|
||||
* @brief encode decoded data
|
||||
*
|
||||
* @param decoded_data
|
||||
* @param decoded_data_size
|
||||
* @param encoded_data
|
||||
* @param encoded_data_size
|
||||
*/
|
||||
virtual void encode(
|
||||
const uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size,
|
||||
uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size) = 0;
|
||||
|
||||
/**
|
||||
* @brief decode encoded data
|
||||
*
|
||||
* @param encoded_data
|
||||
* @param encoded_data_size
|
||||
* @param decoded_data
|
||||
* @param decoded_data_size
|
||||
*/
|
||||
virtual void decode(
|
||||
const uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size,
|
||||
uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size) = 0;
|
||||
|
||||
/**
|
||||
* @brief fast check that data can be correctly decoded
|
||||
*
|
||||
* @param encoded_data
|
||||
* @param encoded_data_size
|
||||
* @return true - can be correctly decoded
|
||||
* @return false - cannot be correctly decoded
|
||||
*/
|
||||
virtual bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) = 0;
|
||||
|
||||
virtual ~ProtocolGeneric(){};
|
||||
};
|
||||
238
applications/lfrfid/helpers/protocols/protocol-hid-h10301.cpp
Normal file
238
applications/lfrfid/helpers/protocols/protocol-hid-h10301.cpp
Normal file
@@ -0,0 +1,238 @@
|
||||
#include "protocol-hid-h10301.h"
|
||||
#include <furi.h>
|
||||
|
||||
typedef uint32_t HID10301CardData;
|
||||
constexpr uint8_t HID10301Count = 3;
|
||||
constexpr uint8_t HID10301BitSize = sizeof(HID10301CardData) * 8;
|
||||
|
||||
static void write_raw_bit(bool bit, uint8_t position, HID10301CardData* card_data) {
|
||||
if(bit) {
|
||||
card_data[position / HID10301BitSize] |=
|
||||
1UL << (HID10301BitSize - (position % HID10301BitSize) - 1);
|
||||
} else {
|
||||
card_data[position / (sizeof(HID10301CardData) * 8)] &=
|
||||
~(1UL << (HID10301BitSize - (position % HID10301BitSize) - 1));
|
||||
}
|
||||
}
|
||||
|
||||
static void write_bit(bool bit, uint8_t position, HID10301CardData* card_data) {
|
||||
write_raw_bit(bit, position + 0, card_data);
|
||||
write_raw_bit(!bit, position + 1, card_data);
|
||||
}
|
||||
|
||||
uint8_t ProtocolHID10301::get_encoded_data_size() {
|
||||
return sizeof(HID10301CardData) * HID10301Count;
|
||||
}
|
||||
|
||||
uint8_t ProtocolHID10301::get_decoded_data_size() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
void ProtocolHID10301::encode(
|
||||
const uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size,
|
||||
uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size) {
|
||||
furi_check(decoded_data_size >= get_decoded_data_size());
|
||||
furi_check(encoded_data_size >= get_encoded_data_size());
|
||||
|
||||
HID10301CardData card_data[HID10301Count] = {0, 0, 0};
|
||||
|
||||
uint32_t fc_cn = (decoded_data[0] << 16) | (decoded_data[1] << 8) | decoded_data[2];
|
||||
|
||||
// even parity sum calculation (high 12 bits of data)
|
||||
uint8_t even_parity_sum = 0;
|
||||
for(int8_t i = 12; i < 24; i++) {
|
||||
if(((fc_cn >> i) & 1) == 1) {
|
||||
even_parity_sum++;
|
||||
}
|
||||
}
|
||||
|
||||
// odd parity sum calculation (low 12 bits of data)
|
||||
uint8_t odd_parity_sum = 1;
|
||||
for(int8_t i = 0; i < 12; i++) {
|
||||
if(((fc_cn >> i) & 1) == 1) {
|
||||
odd_parity_sum++;
|
||||
}
|
||||
}
|
||||
|
||||
// 0x1D preamble
|
||||
write_raw_bit(0, 0, card_data);
|
||||
write_raw_bit(0, 1, card_data);
|
||||
write_raw_bit(0, 2, card_data);
|
||||
write_raw_bit(1, 3, card_data);
|
||||
write_raw_bit(1, 4, card_data);
|
||||
write_raw_bit(1, 5, card_data);
|
||||
write_raw_bit(0, 6, card_data);
|
||||
write_raw_bit(1, 7, card_data);
|
||||
|
||||
// company / OEM code 1
|
||||
write_bit(0, 8, card_data);
|
||||
write_bit(0, 10, card_data);
|
||||
write_bit(0, 12, card_data);
|
||||
write_bit(0, 14, card_data);
|
||||
write_bit(0, 16, card_data);
|
||||
write_bit(0, 18, card_data);
|
||||
write_bit(1, 20, card_data);
|
||||
|
||||
// card format / length 1
|
||||
write_bit(0, 22, card_data);
|
||||
write_bit(0, 24, card_data);
|
||||
write_bit(0, 26, card_data);
|
||||
write_bit(0, 28, card_data);
|
||||
write_bit(0, 30, card_data);
|
||||
write_bit(0, 32, card_data);
|
||||
write_bit(0, 34, card_data);
|
||||
write_bit(0, 36, card_data);
|
||||
write_bit(0, 38, card_data);
|
||||
write_bit(0, 40, card_data);
|
||||
write_bit(1, 42, card_data);
|
||||
|
||||
// even parity bit
|
||||
write_bit((even_parity_sum % 2), 44, card_data);
|
||||
|
||||
// data
|
||||
for(uint8_t i = 0; i < 24; i++) {
|
||||
write_bit((fc_cn >> (23 - i)) & 1, 46 + (i * 2), card_data);
|
||||
}
|
||||
|
||||
// odd parity bit
|
||||
write_bit((odd_parity_sum % 2), 94, card_data);
|
||||
|
||||
memcpy(encoded_data, &card_data, get_encoded_data_size());
|
||||
}
|
||||
|
||||
void ProtocolHID10301::decode(
|
||||
const uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size,
|
||||
uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size) {
|
||||
furi_check(decoded_data_size >= get_decoded_data_size());
|
||||
furi_check(encoded_data_size >= get_encoded_data_size());
|
||||
|
||||
const HID10301CardData* card_data = reinterpret_cast<const HID10301CardData*>(encoded_data);
|
||||
|
||||
// data decoding
|
||||
uint32_t result = 0;
|
||||
|
||||
// decode from word 1
|
||||
// coded with 01 = 0, 10 = 1 transitions
|
||||
for(int8_t i = 9; i >= 0; i--) {
|
||||
switch((*(card_data + 1) >> (2 * i)) & 0b11) {
|
||||
case 0b01:
|
||||
result = (result << 1) | 0;
|
||||
break;
|
||||
case 0b10:
|
||||
result = (result << 1) | 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// decode from word 2
|
||||
// coded with 01 = 0, 10 = 1 transitions
|
||||
for(int8_t i = 15; i >= 0; i--) {
|
||||
switch((*(card_data + 2) >> (2 * i)) & 0b11) {
|
||||
case 0b01:
|
||||
result = (result << 1) | 0;
|
||||
break;
|
||||
case 0b10:
|
||||
result = (result << 1) | 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t data[3] = {(uint8_t)(result >> 17), (uint8_t)(result >> 9), (uint8_t)(result >> 1)};
|
||||
|
||||
memcpy(decoded_data, &data, get_decoded_data_size());
|
||||
}
|
||||
|
||||
bool ProtocolHID10301::can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) {
|
||||
furi_check(encoded_data_size >= get_encoded_data_size());
|
||||
|
||||
const HID10301CardData* card_data = reinterpret_cast<const HID10301CardData*>(encoded_data);
|
||||
|
||||
// packet preamble
|
||||
// raw data
|
||||
if(*(encoded_data + 3) != 0x1D) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// encoded company/oem
|
||||
// coded with 01 = 0, 10 = 1 transitions
|
||||
// stored in word 0
|
||||
if((*card_data >> 10 & 0x3FFF) != 0x1556) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// encoded format/length
|
||||
// coded with 01 = 0, 10 = 1 transitions
|
||||
// stored in word 0 and word 1
|
||||
if((((*card_data & 0x3FF) << 12) | ((*(card_data + 1) >> 20) & 0xFFF)) != 0x155556) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// data decoding
|
||||
uint32_t result = 0;
|
||||
|
||||
// decode from word 1
|
||||
// coded with 01 = 0, 10 = 1 transitions
|
||||
for(int8_t i = 9; i >= 0; i--) {
|
||||
switch((*(card_data + 1) >> (2 * i)) & 0b11) {
|
||||
case 0b01:
|
||||
result = (result << 1) | 0;
|
||||
break;
|
||||
case 0b10:
|
||||
result = (result << 1) | 1;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// decode from word 2
|
||||
// coded with 01 = 0, 10 = 1 transitions
|
||||
for(int8_t i = 15; i >= 0; i--) {
|
||||
switch((*(card_data + 2) >> (2 * i)) & 0b11) {
|
||||
case 0b01:
|
||||
result = (result << 1) | 0;
|
||||
break;
|
||||
case 0b10:
|
||||
result = (result << 1) | 1;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// trailing parity (odd) test
|
||||
uint8_t parity_sum = 0;
|
||||
for(int8_t i = 0; i < 13; i++) {
|
||||
if(((result >> i) & 1) == 1) {
|
||||
parity_sum++;
|
||||
}
|
||||
}
|
||||
|
||||
if((parity_sum % 2) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// leading parity (even) test
|
||||
parity_sum = 0;
|
||||
for(int8_t i = 13; i < 26; i++) {
|
||||
if(((result >> i) & 1) == 1) {
|
||||
parity_sum++;
|
||||
}
|
||||
}
|
||||
|
||||
if((parity_sum % 2) == 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
22
applications/lfrfid/helpers/protocols/protocol-hid-h10301.h
Normal file
22
applications/lfrfid/helpers/protocols/protocol-hid-h10301.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include "protocol-generic.h"
|
||||
|
||||
class ProtocolHID10301 : public ProtocolGeneric {
|
||||
public:
|
||||
uint8_t get_encoded_data_size() final;
|
||||
uint8_t get_decoded_data_size() final;
|
||||
|
||||
void encode(
|
||||
const uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size,
|
||||
uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size) final;
|
||||
|
||||
void decode(
|
||||
const uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size,
|
||||
uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size) final;
|
||||
|
||||
bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final;
|
||||
};
|
||||
131
applications/lfrfid/helpers/protocols/protocol-indala-40134.cpp
Normal file
131
applications/lfrfid/helpers/protocols/protocol-indala-40134.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
#include "protocol-indala-40134.h"
|
||||
#include <furi.h>
|
||||
|
||||
typedef uint64_t Indala40134CardData;
|
||||
|
||||
static void set_bit(bool bit, uint8_t position, Indala40134CardData* card_data) {
|
||||
position = (sizeof(Indala40134CardData) * 8) - 1 - position;
|
||||
if(bit) {
|
||||
*card_data |= 1ull << position;
|
||||
} else {
|
||||
*card_data &= ~(1ull << position);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t ProtocolIndala40134::get_encoded_data_size() {
|
||||
return sizeof(Indala40134CardData);
|
||||
}
|
||||
|
||||
uint8_t ProtocolIndala40134::get_decoded_data_size() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
void ProtocolIndala40134::encode(
|
||||
const uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size,
|
||||
uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size) {
|
||||
furi_check(decoded_data_size >= get_decoded_data_size());
|
||||
furi_check(encoded_data_size >= get_encoded_data_size());
|
||||
|
||||
uint32_t fc_and_card = (decoded_data[0] << 16) | (decoded_data[1] << 8) | decoded_data[2];
|
||||
Indala40134CardData card_data = 0;
|
||||
|
||||
// preamble
|
||||
set_bit(1, 0, &card_data);
|
||||
set_bit(1, 2, &card_data);
|
||||
set_bit(1, 32, &card_data);
|
||||
|
||||
// factory code
|
||||
set_bit(((fc_and_card >> 23) & 1), 57, &card_data);
|
||||
set_bit(((fc_and_card >> 22) & 1), 49, &card_data);
|
||||
set_bit(((fc_and_card >> 21) & 1), 44, &card_data);
|
||||
set_bit(((fc_and_card >> 20) & 1), 47, &card_data);
|
||||
set_bit(((fc_and_card >> 19) & 1), 48, &card_data);
|
||||
set_bit(((fc_and_card >> 18) & 1), 53, &card_data);
|
||||
set_bit(((fc_and_card >> 17) & 1), 39, &card_data);
|
||||
set_bit(((fc_and_card >> 16) & 1), 58, &card_data);
|
||||
|
||||
// card number
|
||||
set_bit(((fc_and_card >> 15) & 1), 42, &card_data);
|
||||
set_bit(((fc_and_card >> 14) & 1), 45, &card_data);
|
||||
set_bit(((fc_and_card >> 13) & 1), 43, &card_data);
|
||||
set_bit(((fc_and_card >> 12) & 1), 40, &card_data);
|
||||
set_bit(((fc_and_card >> 11) & 1), 52, &card_data);
|
||||
set_bit(((fc_and_card >> 10) & 1), 36, &card_data);
|
||||
set_bit(((fc_and_card >> 9) & 1), 35, &card_data);
|
||||
set_bit(((fc_and_card >> 8) & 1), 51, &card_data);
|
||||
set_bit(((fc_and_card >> 7) & 1), 46, &card_data);
|
||||
set_bit(((fc_and_card >> 6) & 1), 33, &card_data);
|
||||
set_bit(((fc_and_card >> 5) & 1), 37, &card_data);
|
||||
set_bit(((fc_and_card >> 4) & 1), 54, &card_data);
|
||||
set_bit(((fc_and_card >> 3) & 1), 56, &card_data);
|
||||
set_bit(((fc_and_card >> 2) & 1), 59, &card_data);
|
||||
set_bit(((fc_and_card >> 1) & 1), 50, &card_data);
|
||||
set_bit(((fc_and_card >> 0) & 1), 41, &card_data);
|
||||
|
||||
// checksum
|
||||
uint8_t checksum = 0;
|
||||
checksum += ((fc_and_card >> 14) & 1);
|
||||
checksum += ((fc_and_card >> 12) & 1);
|
||||
checksum += ((fc_and_card >> 9) & 1);
|
||||
checksum += ((fc_and_card >> 8) & 1);
|
||||
checksum += ((fc_and_card >> 6) & 1);
|
||||
checksum += ((fc_and_card >> 5) & 1);
|
||||
checksum += ((fc_and_card >> 2) & 1);
|
||||
checksum += ((fc_and_card >> 0) & 1);
|
||||
|
||||
// wiegand parity bits
|
||||
// even parity sum calculation (high 12 bits of data)
|
||||
uint8_t even_parity_sum = 0;
|
||||
for(int8_t i = 12; i < 24; i++) {
|
||||
if(((fc_and_card >> i) & 1) == 1) {
|
||||
even_parity_sum++;
|
||||
}
|
||||
}
|
||||
|
||||
// odd parity sum calculation (low 12 bits of data)
|
||||
uint8_t odd_parity_sum = 1;
|
||||
for(int8_t i = 0; i < 12; i++) {
|
||||
if(((fc_and_card >> i) & 1) == 1) {
|
||||
odd_parity_sum++;
|
||||
}
|
||||
}
|
||||
|
||||
// even parity bit
|
||||
set_bit((even_parity_sum % 2), 34, &card_data);
|
||||
|
||||
// odd parity bit
|
||||
set_bit((odd_parity_sum % 2), 38, &card_data);
|
||||
|
||||
// checksum
|
||||
if((checksum & 1) == 1) {
|
||||
set_bit(0, 62, &card_data);
|
||||
set_bit(1, 63, &card_data);
|
||||
} else {
|
||||
set_bit(1, 62, &card_data);
|
||||
set_bit(0, 63, &card_data);
|
||||
}
|
||||
|
||||
memcpy(encoded_data, &card_data, get_encoded_data_size());
|
||||
}
|
||||
|
||||
void ProtocolIndala40134::decode(
|
||||
const uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size,
|
||||
uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size) {
|
||||
furi_check(decoded_data_size >= get_decoded_data_size());
|
||||
furi_check(encoded_data_size >= get_encoded_data_size());
|
||||
// TODO implement decoding
|
||||
furi_check(0);
|
||||
}
|
||||
|
||||
bool ProtocolIndala40134::can_be_decoded(
|
||||
const uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size) {
|
||||
furi_check(encoded_data_size >= get_encoded_data_size());
|
||||
// TODO implement decoding
|
||||
furi_check(0);
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include "protocol-generic.h"
|
||||
|
||||
class ProtocolIndala40134 : public ProtocolGeneric {
|
||||
public:
|
||||
uint8_t get_encoded_data_size() final;
|
||||
uint8_t get_decoded_data_size() final;
|
||||
|
||||
void encode(
|
||||
const uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size,
|
||||
uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size) final;
|
||||
|
||||
void decode(
|
||||
const uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size,
|
||||
uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size) final;
|
||||
|
||||
bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final;
|
||||
};
|
||||
95
applications/lfrfid/helpers/pulse-joiner.cpp
Normal file
95
applications/lfrfid/helpers/pulse-joiner.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
#include "pulse-joiner.h"
|
||||
#include <furi.h>
|
||||
|
||||
bool PulseJoiner::push_pulse(bool polarity, uint16_t period, uint16_t pulse) {
|
||||
bool result = false;
|
||||
furi_check((pulse_index + 1) < pulse_max);
|
||||
|
||||
if(polarity == false && pulse_index == 0) {
|
||||
// first negative pulse is ommited
|
||||
|
||||
} else {
|
||||
pulses[pulse_index].polarity = polarity;
|
||||
pulses[pulse_index].time = pulse;
|
||||
pulse_index++;
|
||||
}
|
||||
|
||||
if(period > pulse) {
|
||||
pulses[pulse_index].polarity = !polarity;
|
||||
pulses[pulse_index].time = period - pulse;
|
||||
pulse_index++;
|
||||
}
|
||||
|
||||
if(pulse_index >= 4) {
|
||||
// we know that first pulse is always high
|
||||
// so we wait 2 edges, hi2low and next low2hi
|
||||
|
||||
uint8_t edges_count = 0;
|
||||
bool last_polarity = pulses[0].polarity;
|
||||
|
||||
for(uint8_t i = 1; i < pulse_index; i++) {
|
||||
if(pulses[i].polarity != last_polarity) {
|
||||
edges_count++;
|
||||
last_polarity = pulses[i].polarity;
|
||||
}
|
||||
}
|
||||
|
||||
if(edges_count >= 2) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void PulseJoiner::pop_pulse(uint16_t* period, uint16_t* pulse) {
|
||||
furi_check(pulse_index <= (pulse_max + 1));
|
||||
|
||||
uint16_t tmp_period = 0;
|
||||
uint16_t tmp_pulse = 0;
|
||||
uint8_t edges_count = 0;
|
||||
bool last_polarity = pulses[0].polarity;
|
||||
uint8_t next_fist_pulse = 0;
|
||||
|
||||
for(uint8_t i = 0; i < pulse_max; i++) {
|
||||
// count edges
|
||||
if(pulses[i].polarity != last_polarity) {
|
||||
edges_count++;
|
||||
last_polarity = pulses[i].polarity;
|
||||
}
|
||||
|
||||
// wait for 2 edges
|
||||
if(edges_count == 2) {
|
||||
next_fist_pulse = i;
|
||||
break;
|
||||
}
|
||||
|
||||
// sum pulse time
|
||||
if(pulses[i].polarity) {
|
||||
tmp_period += pulses[i].time;
|
||||
tmp_pulse += pulses[i].time;
|
||||
} else {
|
||||
tmp_period += pulses[i].time;
|
||||
}
|
||||
pulse_index--;
|
||||
}
|
||||
|
||||
*period = tmp_period;
|
||||
*pulse = tmp_pulse;
|
||||
|
||||
// remove counted periods and shift data
|
||||
for(uint8_t i = 0; i < pulse_max; i++) {
|
||||
if((next_fist_pulse + i) < pulse_max) {
|
||||
pulses[i].polarity = pulses[next_fist_pulse + i].polarity;
|
||||
pulses[i].time = pulses[next_fist_pulse + i].time;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PulseJoiner::PulseJoiner() {
|
||||
for(uint8_t i = 0; i < pulse_max; i++) {
|
||||
pulses[i] = {false, 0};
|
||||
}
|
||||
}
|
||||
36
applications/lfrfid/helpers/pulse-joiner.h
Normal file
36
applications/lfrfid/helpers/pulse-joiner.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
#include "stdint.h"
|
||||
|
||||
class PulseJoiner {
|
||||
public:
|
||||
/**
|
||||
* @brief Push timer pulse. First negative pulse is ommited.
|
||||
*
|
||||
* @param polarity pulse polarity: true = high2low, false = low2high
|
||||
* @param period overall period time in timer clicks
|
||||
* @param pulse pulse time in timer clicks
|
||||
*
|
||||
* @return true - next pulse can and must be popped immediatly
|
||||
*/
|
||||
bool push_pulse(bool polarity, uint16_t period, uint16_t pulse);
|
||||
|
||||
/**
|
||||
* @brief Get the next timer pulse. Call only if push_pulse returns true.
|
||||
*
|
||||
* @param period overall period time in timer clicks
|
||||
* @param pulse pulse time in timer clicks
|
||||
*/
|
||||
void pop_pulse(uint16_t* period, uint16_t* pulse);
|
||||
|
||||
PulseJoiner();
|
||||
|
||||
private:
|
||||
struct Pulse {
|
||||
bool polarity;
|
||||
uint16_t time;
|
||||
};
|
||||
|
||||
uint8_t pulse_index = 0;
|
||||
static const uint8_t pulse_max = 6;
|
||||
Pulse pulses[pulse_max];
|
||||
};
|
||||
44
applications/lfrfid/helpers/rfid-key.cpp
Normal file
44
applications/lfrfid/helpers/rfid-key.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#include "rfid-key.h"
|
||||
#include <furi/check.h>
|
||||
|
||||
RfidKey::RfidKey() {
|
||||
data.fill(0);
|
||||
|
||||
for(uint8_t i = 0; i < (LFRFID_KEY_NAME_SIZE + 1); i++) {
|
||||
name[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
RfidKey::~RfidKey() {
|
||||
}
|
||||
|
||||
void RfidKey::set_type(LfrfidKeyType _type) {
|
||||
type = _type;
|
||||
}
|
||||
|
||||
void RfidKey::set_data(uint8_t* _data, const uint8_t _data_size) {
|
||||
furi_assert(_data_size <= data.size());
|
||||
for(uint8_t i = 0; i < _data_size; i++) {
|
||||
data[i] = _data[i];
|
||||
}
|
||||
}
|
||||
|
||||
LfrfidKeyType RfidKey::get_type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
uint8_t* RfidKey::get_data() {
|
||||
return &data[0];
|
||||
}
|
||||
|
||||
const char* RfidKey::get_type_text() {
|
||||
return lfrfid_key_get_type_string(type);
|
||||
}
|
||||
|
||||
const uint8_t RfidKey::get_type_data_count() {
|
||||
return lfrfid_key_get_type_data_count(type);
|
||||
}
|
||||
|
||||
char* RfidKey::get_name() {
|
||||
return name;
|
||||
}
|
||||
25
applications/lfrfid/helpers/rfid-key.h
Normal file
25
applications/lfrfid/helpers/rfid-key.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include "key-info.h"
|
||||
#include <array>
|
||||
|
||||
class RfidKey {
|
||||
public:
|
||||
RfidKey();
|
||||
~RfidKey();
|
||||
|
||||
void set_type(LfrfidKeyType type);
|
||||
void set_data(uint8_t* data, const uint8_t data_size);
|
||||
|
||||
LfrfidKeyType get_type();
|
||||
uint8_t* get_data();
|
||||
|
||||
const char* get_type_text();
|
||||
const uint8_t get_type_data_count();
|
||||
|
||||
char* get_name();
|
||||
|
||||
private:
|
||||
std::array<uint8_t, LFRFID_KEY_SIZE> data;
|
||||
LfrfidKeyType type;
|
||||
char name[LFRFID_KEY_NAME_SIZE + 1];
|
||||
};
|
||||
35
applications/lfrfid/helpers/rfid-name-generator.cpp
Normal file
35
applications/lfrfid/helpers/rfid-name-generator.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include "rfid-name-generator.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void rfid_generate_random_name(char* name, uint8_t max_name_size) {
|
||||
const uint8_t prefix_size = 9;
|
||||
const char* prefix[prefix_size] = {
|
||||
"good",
|
||||
"nice",
|
||||
"best",
|
||||
"some",
|
||||
"strange",
|
||||
"working",
|
||||
"that",
|
||||
"forgettable",
|
||||
"easy",
|
||||
};
|
||||
|
||||
const uint8_t suffix_size = 7;
|
||||
const char* suffix[suffix_size] = {
|
||||
"pass",
|
||||
"card",
|
||||
"key",
|
||||
"fob",
|
||||
"permit",
|
||||
"pass",
|
||||
"one",
|
||||
};
|
||||
|
||||
sniprintf(
|
||||
name, max_name_size, "%s_%s", prefix[rand() % prefix_size], suffix[rand() % suffix_size]);
|
||||
|
||||
// to upper
|
||||
name[0] = name[0] - ('a' - 'A');
|
||||
}
|
||||
4
applications/lfrfid/helpers/rfid-name-generator.h
Normal file
4
applications/lfrfid/helpers/rfid-name-generator.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include "stdint.h"
|
||||
|
||||
void rfid_generate_random_name(char* name, uint8_t max_name_size);
|
||||
134
applications/lfrfid/helpers/rfid-reader.cpp
Normal file
134
applications/lfrfid/helpers/rfid-reader.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
#include "rfid-reader.h"
|
||||
#include <furi.h>
|
||||
#include <api-hal.h>
|
||||
#include <stm32wbxx_ll_cortex.h>
|
||||
#include <tim.h>
|
||||
|
||||
extern COMP_HandleTypeDef hcomp1;
|
||||
|
||||
/**
|
||||
* @brief private violation assistant for RfidReader
|
||||
*/
|
||||
struct RfidReaderAccessor {
|
||||
static void decode(RfidReader& rfid_reader, bool polarity) {
|
||||
rfid_reader.decode(polarity);
|
||||
}
|
||||
};
|
||||
|
||||
void RfidReader::decode(bool polarity) {
|
||||
uint32_t current_dwt_value = DWT->CYCCNT;
|
||||
|
||||
switch(type) {
|
||||
case Type::Normal:
|
||||
decoder_em.process_front(polarity, current_dwt_value - last_dwt_value);
|
||||
decoder_hid26.process_front(polarity, current_dwt_value - last_dwt_value);
|
||||
//decoder_indala.process_front(polarity, current_dwt_value - last_dwt_value);
|
||||
//decoder_analyzer.process_front(polarity, current_dwt_value - last_dwt_value);
|
||||
|
||||
last_dwt_value = current_dwt_value;
|
||||
break;
|
||||
case Type::Indala:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void comparator_trigger_callback(void* hcomp, void* comp_ctx) {
|
||||
COMP_HandleTypeDef* _hcomp = static_cast<COMP_HandleTypeDef*>(hcomp);
|
||||
RfidReader* _this = static_cast<RfidReader*>(comp_ctx);
|
||||
|
||||
if(hcomp == &hcomp1) {
|
||||
RfidReaderAccessor::decode(
|
||||
*_this, (HAL_COMP_GetOutputLevel(_hcomp) == COMP_OUTPUT_LEVEL_HIGH));
|
||||
}
|
||||
}
|
||||
|
||||
RfidReader::RfidReader() {
|
||||
}
|
||||
|
||||
void RfidReader::start(Type _type) {
|
||||
type = _type;
|
||||
|
||||
start_gpio();
|
||||
switch(type) {
|
||||
case Type::Normal:
|
||||
start_timer();
|
||||
break;
|
||||
case Type::Indala:
|
||||
start_timer_indala();
|
||||
break;
|
||||
}
|
||||
|
||||
start_comparator();
|
||||
}
|
||||
|
||||
void RfidReader::stop() {
|
||||
stop_gpio();
|
||||
stop_timer();
|
||||
stop_comparator();
|
||||
}
|
||||
|
||||
bool RfidReader::read(LfrfidKeyType* type, uint8_t* data, uint8_t data_size) {
|
||||
bool result = false;
|
||||
|
||||
if(decoder_em.read(data, data_size)) {
|
||||
*type = LfrfidKeyType::KeyEM4100;
|
||||
result = true;
|
||||
}
|
||||
|
||||
if(decoder_hid26.read(data, data_size)) {
|
||||
*type = LfrfidKeyType::KeyH10301;
|
||||
result = true;
|
||||
}
|
||||
|
||||
//decoder_indala.read(NULL, 0);
|
||||
//decoder_analyzer.read(NULL, 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void RfidReader::start_comparator(void) {
|
||||
api_interrupt_add(comparator_trigger_callback, InterruptTypeComparatorTrigger, this);
|
||||
last_dwt_value = DWT->CYCCNT;
|
||||
|
||||
hcomp1.Init.InputMinus = COMP_INPUT_MINUS_1_2VREFINT;
|
||||
hcomp1.Init.InputPlus = COMP_INPUT_PLUS_IO1;
|
||||
hcomp1.Init.OutputPol = COMP_OUTPUTPOL_NONINVERTED;
|
||||
hcomp1.Init.Hysteresis = COMP_HYSTERESIS_LOW;
|
||||
hcomp1.Init.BlankingSrce = COMP_BLANKINGSRC_NONE;
|
||||
hcomp1.Init.Mode = COMP_POWERMODE_MEDIUMSPEED;
|
||||
hcomp1.Init.WindowMode = COMP_WINDOWMODE_DISABLE;
|
||||
hcomp1.Init.TriggerMode = COMP_TRIGGERMODE_IT_RISING_FALLING;
|
||||
if(HAL_COMP_Init(&hcomp1) != HAL_OK) {
|
||||
Error_Handler();
|
||||
}
|
||||
|
||||
HAL_COMP_Start(&hcomp1);
|
||||
}
|
||||
|
||||
void RfidReader::start_timer(void) {
|
||||
api_hal_rfid_tim_read(125000, 0.5);
|
||||
api_hal_rfid_tim_read_start();
|
||||
}
|
||||
|
||||
void RfidReader::start_timer_indala(void) {
|
||||
api_hal_rfid_tim_read(62500, 0.25);
|
||||
api_hal_rfid_tim_read_start();
|
||||
}
|
||||
|
||||
void RfidReader::start_gpio(void) {
|
||||
api_hal_rfid_pins_read();
|
||||
}
|
||||
|
||||
void RfidReader::stop_comparator(void) {
|
||||
HAL_COMP_Stop(&hcomp1);
|
||||
api_interrupt_remove(comparator_trigger_callback, InterruptTypeComparatorTrigger);
|
||||
}
|
||||
|
||||
void RfidReader::stop_timer(void) {
|
||||
api_hal_rfid_tim_read_stop();
|
||||
api_hal_rfid_tim_reset();
|
||||
}
|
||||
|
||||
void RfidReader::stop_gpio(void) {
|
||||
api_hal_rfid_pins_reset();
|
||||
}
|
||||
41
applications/lfrfid/helpers/rfid-reader.h
Normal file
41
applications/lfrfid/helpers/rfid-reader.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
#include "decoder-analyzer.h"
|
||||
#include "decoder-emmarine.h"
|
||||
#include "decoder-hid26.h"
|
||||
#include "decoder-indala.h"
|
||||
#include "key-info.h"
|
||||
|
||||
class RfidReader {
|
||||
public:
|
||||
enum class Type : uint8_t {
|
||||
Normal,
|
||||
Indala,
|
||||
};
|
||||
|
||||
RfidReader();
|
||||
void start(Type type);
|
||||
void stop();
|
||||
bool read(LfrfidKeyType* type, uint8_t* data, uint8_t data_size);
|
||||
|
||||
private:
|
||||
friend struct RfidReaderAccessor;
|
||||
|
||||
//DecoderAnalyzer decoder_analyzer;
|
||||
DecoderEMMarine decoder_em;
|
||||
DecoderHID26 decoder_hid26;
|
||||
DecoderIndala decoder_indala;
|
||||
|
||||
uint32_t last_dwt_value;
|
||||
|
||||
void start_comparator(void);
|
||||
void start_timer(void);
|
||||
void start_timer_indala(void);
|
||||
void start_gpio(void);
|
||||
void stop_comparator(void);
|
||||
void stop_timer(void);
|
||||
void stop_gpio(void);
|
||||
|
||||
void decode(bool polarity);
|
||||
|
||||
Type type = Type::Normal;
|
||||
};
|
||||
64
applications/lfrfid/helpers/rfid-timer-emulator.cpp
Normal file
64
applications/lfrfid/helpers/rfid-timer-emulator.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "rfid-timer-emulator.h"
|
||||
|
||||
extern TIM_HandleTypeDef htim1;
|
||||
|
||||
RfidTimerEmulator::RfidTimerEmulator() {
|
||||
}
|
||||
|
||||
RfidTimerEmulator::~RfidTimerEmulator() {
|
||||
std::map<LfrfidKeyType, EncoderGeneric*>::iterator it;
|
||||
|
||||
for(it = encoders.begin(); it != encoders.end(); ++it) {
|
||||
delete it->second;
|
||||
encoders.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void RfidTimerEmulator::start(LfrfidKeyType type, const uint8_t* data, uint8_t data_size) {
|
||||
if(encoders.count(type)) {
|
||||
current_encoder = encoders.find(type)->second;
|
||||
|
||||
if(data_size >= lfrfid_key_get_type_data_count(type)) {
|
||||
current_encoder->init(data, data_size);
|
||||
|
||||
api_hal_rfid_tim_emulate(125000);
|
||||
api_hal_rfid_pins_emulate();
|
||||
|
||||
api_interrupt_add(timer_update_callback, InterruptTypeTimerUpdate, this);
|
||||
|
||||
api_hal_rfid_tim_emulate_start();
|
||||
}
|
||||
} else {
|
||||
// not found
|
||||
}
|
||||
}
|
||||
|
||||
void RfidTimerEmulator::stop() {
|
||||
api_hal_rfid_tim_emulate_stop();
|
||||
api_interrupt_remove(timer_update_callback, InterruptTypeTimerUpdate);
|
||||
|
||||
api_hal_rfid_tim_reset();
|
||||
api_hal_rfid_pins_reset();
|
||||
}
|
||||
|
||||
void RfidTimerEmulator::timer_update_callback(void* _hw, void* ctx) {
|
||||
RfidTimerEmulator* _this = static_cast<RfidTimerEmulator*>(ctx);
|
||||
TIM_HandleTypeDef* hw = static_cast<TIM_HandleTypeDef*>(_hw);
|
||||
|
||||
if(api_hal_rfid_is_tim_emulate(hw)) {
|
||||
bool result;
|
||||
bool polarity;
|
||||
uint16_t period;
|
||||
uint16_t pulse;
|
||||
|
||||
do {
|
||||
_this->current_encoder->get_next(&polarity, &period, &pulse);
|
||||
result = _this->pulse_joiner.push_pulse(polarity, period, pulse);
|
||||
} while(result == false);
|
||||
|
||||
_this->pulse_joiner.pop_pulse(&period, &pulse);
|
||||
|
||||
api_hal_rfid_set_emulate_period(period - 1);
|
||||
api_hal_rfid_set_emulate_pulse(pulse);
|
||||
}
|
||||
}
|
||||
29
applications/lfrfid/helpers/rfid-timer-emulator.h
Normal file
29
applications/lfrfid/helpers/rfid-timer-emulator.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
#include <api-hal.h>
|
||||
#include "key-info.h"
|
||||
#include "encoder-generic.h"
|
||||
#include "encoder-emmarine.h"
|
||||
#include "encoder-hid-h10301.h"
|
||||
#include "encoder-indala-40134.h"
|
||||
#include "pulse-joiner.h"
|
||||
#include <map>
|
||||
|
||||
class RfidTimerEmulator {
|
||||
public:
|
||||
RfidTimerEmulator();
|
||||
~RfidTimerEmulator();
|
||||
void start(LfrfidKeyType type, const uint8_t* data, uint8_t data_size);
|
||||
void stop();
|
||||
|
||||
private:
|
||||
EncoderGeneric* current_encoder = nullptr;
|
||||
|
||||
std::map<LfrfidKeyType, EncoderGeneric*> encoders = {
|
||||
{LfrfidKeyType::KeyEM4100, new EncoderEM()},
|
||||
{LfrfidKeyType::KeyH10301, new EncoderHID_H10301()},
|
||||
{LfrfidKeyType::KeyI40134, new EncoderIndala_40134()},
|
||||
};
|
||||
|
||||
PulseJoiner pulse_joiner;
|
||||
static void timer_update_callback(void* _hw, void* ctx);
|
||||
};
|
||||
111
applications/lfrfid/helpers/rfid-worker.cpp
Normal file
111
applications/lfrfid/helpers/rfid-worker.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
#include "rfid-worker.h"
|
||||
|
||||
RfidWorker::RfidWorker() {
|
||||
}
|
||||
|
||||
RfidWorker::~RfidWorker() {
|
||||
}
|
||||
|
||||
void RfidWorker::start_read() {
|
||||
reader.start(RfidReader::Type::Normal);
|
||||
}
|
||||
|
||||
bool RfidWorker::read() {
|
||||
static const uint8_t data_size = LFRFID_KEY_SIZE;
|
||||
uint8_t data[data_size] = {0};
|
||||
LfrfidKeyType type;
|
||||
|
||||
bool result = reader.read(&type, data, data_size);
|
||||
|
||||
if(result) {
|
||||
key.set_type(type);
|
||||
key.set_data(data, data_size);
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void RfidWorker::stop_read() {
|
||||
reader.stop();
|
||||
}
|
||||
|
||||
void RfidWorker::start_write() {
|
||||
write_result = WriteResult::Nothing;
|
||||
write_sequence = new TickSequencer();
|
||||
validate_counts = 0;
|
||||
|
||||
write_sequence->do_every_tick(1, std::bind(&RfidWorker::sq_write, this));
|
||||
write_sequence->do_after_tick(2, std::bind(&RfidWorker::sq_write_start_validate, this));
|
||||
write_sequence->do_after_tick(15, std::bind(&RfidWorker::sq_write_validate, this));
|
||||
write_sequence->do_every_tick(1, std::bind(&RfidWorker::sq_write_stop_validate, this));
|
||||
}
|
||||
|
||||
RfidWorker::WriteResult RfidWorker::write() {
|
||||
write_sequence->tick();
|
||||
return write_result;
|
||||
}
|
||||
|
||||
void RfidWorker::stop_write() {
|
||||
delete write_sequence;
|
||||
reader.stop();
|
||||
}
|
||||
|
||||
void RfidWorker::start_emulate() {
|
||||
emulator.start(key.get_type(), key.get_data(), key.get_type_data_count());
|
||||
}
|
||||
|
||||
void RfidWorker::stop_emulate() {
|
||||
emulator.stop();
|
||||
}
|
||||
|
||||
void RfidWorker::sq_write() {
|
||||
// TODO expand this
|
||||
switch(key.get_type()) {
|
||||
case LfrfidKeyType::KeyEM4100:
|
||||
writer.start();
|
||||
writer.write_em(key.get_data());
|
||||
writer.stop();
|
||||
break;
|
||||
case LfrfidKeyType::KeyH10301:
|
||||
writer.start();
|
||||
writer.write_hid(key.get_data());
|
||||
writer.stop();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void RfidWorker::sq_write_start_validate() {
|
||||
reader.start(RfidReader::Type::Normal);
|
||||
}
|
||||
|
||||
void RfidWorker::sq_write_validate() {
|
||||
static const uint8_t data_size = LFRFID_KEY_SIZE;
|
||||
uint8_t data[data_size] = {0};
|
||||
LfrfidKeyType type;
|
||||
|
||||
bool result = reader.read(&type, data, data_size);
|
||||
|
||||
if(result) {
|
||||
if(type == key.get_type()) {
|
||||
if(memcmp(data, key.get_data(), key.get_type_data_count()) == 0) {
|
||||
write_result = WriteResult::Ok;
|
||||
validate_counts = 0;
|
||||
} else {
|
||||
validate_counts++;
|
||||
}
|
||||
} else {
|
||||
validate_counts++;
|
||||
}
|
||||
|
||||
if(validate_counts > 5) {
|
||||
write_result = WriteResult::NotWritable;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void RfidWorker::sq_write_stop_validate() {
|
||||
reader.stop();
|
||||
}
|
||||
46
applications/lfrfid/helpers/rfid-worker.h
Normal file
46
applications/lfrfid/helpers/rfid-worker.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
#include "key-info.h"
|
||||
#include "rfid-reader.h"
|
||||
#include "rfid-writer.h"
|
||||
#include "rfid-timer-emulator.h"
|
||||
#include "rfid-key.h"
|
||||
#include "state-sequencer.h"
|
||||
|
||||
class RfidWorker {
|
||||
public:
|
||||
RfidWorker();
|
||||
~RfidWorker();
|
||||
|
||||
void start_read();
|
||||
bool read();
|
||||
void stop_read();
|
||||
|
||||
enum class WriteResult : uint8_t {
|
||||
Ok,
|
||||
NotWritable,
|
||||
Nothing,
|
||||
};
|
||||
|
||||
void start_write();
|
||||
WriteResult write();
|
||||
void stop_write();
|
||||
|
||||
void start_emulate();
|
||||
void stop_emulate();
|
||||
|
||||
RfidKey key;
|
||||
|
||||
private:
|
||||
RfidWriter writer;
|
||||
RfidReader reader;
|
||||
RfidTimerEmulator emulator;
|
||||
|
||||
WriteResult write_result;
|
||||
TickSequencer* write_sequence;
|
||||
|
||||
void sq_write();
|
||||
void sq_write_start_validate();
|
||||
void sq_write_validate();
|
||||
uint8_t validate_counts;
|
||||
void sq_write_stop_validate();
|
||||
};
|
||||
142
applications/lfrfid/helpers/rfid-writer.cpp
Normal file
142
applications/lfrfid/helpers/rfid-writer.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
#include "rfid-writer.h"
|
||||
#include <api-hal.h>
|
||||
#include "protocols/protocol-emmarin.h"
|
||||
#include "protocols/protocol-hid-h10301.h"
|
||||
|
||||
extern COMP_HandleTypeDef hcomp1;
|
||||
|
||||
/**
|
||||
* @brief all timings are specified in field clocks (field clock = 125 kHz, 8 us)
|
||||
*
|
||||
*/
|
||||
class T55xxTiming {
|
||||
public:
|
||||
constexpr static const uint16_t wait_time = 400;
|
||||
constexpr static const uint8_t start_gap = 30;
|
||||
constexpr static const uint8_t write_gap = 18;
|
||||
constexpr static const uint8_t data_0 = 24;
|
||||
constexpr static const uint8_t data_1 = 56;
|
||||
constexpr static const uint16_t program = 700;
|
||||
};
|
||||
|
||||
class T55xxCmd {
|
||||
public:
|
||||
constexpr static const uint8_t opcode_page_0 = 0b10;
|
||||
constexpr static const uint8_t opcode_page_1 = 0b11;
|
||||
constexpr static const uint8_t opcode_reset = 0b00;
|
||||
};
|
||||
|
||||
RfidWriter::RfidWriter() {
|
||||
}
|
||||
|
||||
RfidWriter::~RfidWriter() {
|
||||
}
|
||||
|
||||
void RfidWriter::start() {
|
||||
api_hal_rfid_tim_read(125000, 0.5);
|
||||
api_hal_rfid_pins_read();
|
||||
api_hal_rfid_tim_read_start();
|
||||
}
|
||||
|
||||
void RfidWriter::stop() {
|
||||
api_hal_rfid_tim_read_stop();
|
||||
api_hal_rfid_tim_reset();
|
||||
api_hal_rfid_pins_reset();
|
||||
}
|
||||
|
||||
void RfidWriter::write_gap(uint32_t gap_time) {
|
||||
api_hal_rfid_tim_read_stop();
|
||||
delay_us(gap_time * 8);
|
||||
api_hal_rfid_tim_read_start();
|
||||
}
|
||||
|
||||
void RfidWriter::write_bit(bool value) {
|
||||
if(value) {
|
||||
delay_us(T55xxTiming::data_1 * 8);
|
||||
} else {
|
||||
delay_us(T55xxTiming::data_0 * 8);
|
||||
}
|
||||
write_gap(T55xxTiming::write_gap);
|
||||
}
|
||||
|
||||
void RfidWriter::write_byte(uint8_t value) {
|
||||
for(uint8_t i = 0; i < 8; i++) {
|
||||
write_bit((value >> i) & 1);
|
||||
}
|
||||
}
|
||||
|
||||
void RfidWriter::write_block(uint8_t page, uint8_t block, bool lock_bit, uint32_t data) {
|
||||
delay_us(T55xxTiming::wait_time * 8);
|
||||
|
||||
// start gap
|
||||
write_gap(T55xxTiming::start_gap);
|
||||
|
||||
// opcode
|
||||
switch(page) {
|
||||
case 0:
|
||||
write_bit(1);
|
||||
write_bit(0);
|
||||
break;
|
||||
case 1:
|
||||
write_bit(1);
|
||||
write_bit(1);
|
||||
break;
|
||||
default:
|
||||
furi_check(false);
|
||||
break;
|
||||
}
|
||||
|
||||
// lock bit
|
||||
write_bit(lock_bit);
|
||||
|
||||
// data
|
||||
for(uint8_t i = 0; i < 32; i++) {
|
||||
write_bit((data >> (31 - i)) & 1);
|
||||
}
|
||||
|
||||
// block address
|
||||
write_bit((block >> 2) & 1);
|
||||
write_bit((block >> 1) & 1);
|
||||
write_bit((block >> 0) & 1);
|
||||
|
||||
delay_us(T55xxTiming::program * 8);
|
||||
|
||||
delay_us(T55xxTiming::wait_time * 8);
|
||||
write_reset();
|
||||
}
|
||||
|
||||
void RfidWriter::write_reset() {
|
||||
write_gap(T55xxTiming::start_gap);
|
||||
write_bit(1);
|
||||
write_bit(0);
|
||||
}
|
||||
|
||||
void RfidWriter::write_em(uint8_t em_data[5]) {
|
||||
ProtocolEMMarin em_card;
|
||||
uint64_t em_encoded_data;
|
||||
em_card.encode(em_data, 5, reinterpret_cast<uint8_t*>(&em_encoded_data), sizeof(uint64_t));
|
||||
const uint32_t em_config_block_data = 0b01100000000101001000000001000000;
|
||||
|
||||
__disable_irq();
|
||||
write_block(0, 0, false, em_config_block_data);
|
||||
write_block(0, 1, false, em_encoded_data);
|
||||
write_block(0, 2, false, em_encoded_data >> 32);
|
||||
write_reset();
|
||||
__enable_irq();
|
||||
}
|
||||
|
||||
void RfidWriter::write_hid(uint8_t hid_data[3]) {
|
||||
ProtocolHID10301 hid_card;
|
||||
uint32_t card_data[3];
|
||||
hid_card.encode(hid_data, 3, reinterpret_cast<uint8_t*>(&card_data), sizeof(card_data) * 3);
|
||||
|
||||
const uint32_t hid_config_block_data = 0b00000000000100000111000001100000;
|
||||
|
||||
__disable_irq();
|
||||
write_block(0, 0, false, hid_config_block_data);
|
||||
write_block(0, 1, false, card_data[0]);
|
||||
write_block(0, 2, false, card_data[1]);
|
||||
write_block(0, 3, false, card_data[2]);
|
||||
write_reset();
|
||||
__enable_irq();
|
||||
}
|
||||
19
applications/lfrfid/helpers/rfid-writer.h
Normal file
19
applications/lfrfid/helpers/rfid-writer.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "stdint.h"
|
||||
|
||||
class RfidWriter {
|
||||
public:
|
||||
RfidWriter();
|
||||
~RfidWriter();
|
||||
void start();
|
||||
void stop();
|
||||
void write_em(uint8_t em_data[5]);
|
||||
void write_hid(uint8_t hid_data[3]);
|
||||
|
||||
private:
|
||||
void write_gap(uint32_t gap_time);
|
||||
void write_bit(bool value);
|
||||
void write_byte(uint8_t value);
|
||||
void write_block(uint8_t page, uint8_t block, bool lock_bit, uint32_t data);
|
||||
void write_reset();
|
||||
};
|
||||
50
applications/lfrfid/helpers/state-sequencer.cpp
Normal file
50
applications/lfrfid/helpers/state-sequencer.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#include "state-sequencer.h"
|
||||
#include "stdio.h"
|
||||
|
||||
TickSequencer::TickSequencer() {
|
||||
}
|
||||
|
||||
TickSequencer::~TickSequencer() {
|
||||
}
|
||||
|
||||
void TickSequencer::tick() {
|
||||
if(tick_count == list_it->first) {
|
||||
tick_count = 0;
|
||||
|
||||
list_it++;
|
||||
if(list_it == list.end()) {
|
||||
list_it = list.begin();
|
||||
}
|
||||
}
|
||||
|
||||
list_it->second();
|
||||
tick_count++;
|
||||
}
|
||||
|
||||
void TickSequencer::reset() {
|
||||
list_it = list.begin();
|
||||
tick_count = 0;
|
||||
}
|
||||
|
||||
void TickSequencer::clear() {
|
||||
list.clear();
|
||||
reset();
|
||||
}
|
||||
|
||||
void TickSequencer::do_every_tick(uint32_t tick_count, std::function<void(void)> fn) {
|
||||
list.push_back(std::make_pair(tick_count, fn));
|
||||
reset();
|
||||
}
|
||||
|
||||
void TickSequencer::do_after_tick(uint32_t tick_count, std::function<void(void)> fn) {
|
||||
if(tick_count > 1) {
|
||||
list.push_back(
|
||||
std::make_pair(tick_count - 1, std::bind(&TickSequencer::do_nothing, this)));
|
||||
}
|
||||
list.push_back(std::make_pair(1, fn));
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void TickSequencer::do_nothing() {
|
||||
}
|
||||
25
applications/lfrfid/helpers/state-sequencer.h
Normal file
25
applications/lfrfid/helpers/state-sequencer.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include "stdint.h"
|
||||
#include <list>
|
||||
#include <functional>
|
||||
|
||||
class TickSequencer {
|
||||
public:
|
||||
TickSequencer();
|
||||
~TickSequencer();
|
||||
|
||||
void tick();
|
||||
void reset();
|
||||
void clear();
|
||||
|
||||
void do_every_tick(uint32_t tick_count, std::function<void(void)> fn);
|
||||
void do_after_tick(uint32_t tick_count, std::function<void(void)> fn);
|
||||
|
||||
private:
|
||||
std::list<std::pair<uint32_t, std::function<void(void)> > > list;
|
||||
std::list<std::pair<uint32_t, std::function<void(void)> > >::iterator list_it;
|
||||
|
||||
uint32_t tick_count;
|
||||
|
||||
void do_nothing();
|
||||
};
|
||||
Reference in New Issue
Block a user