Просмотр исходного кода

[FL-1449] Indala reading and writing (#616)

* Rfid: indala 40134 validation and decoding
* Rfid: show indala info
* Rfid: decoder to output comparator signal on gpio pins
* Rfid: working indala 40134 decoder
* HAL: added function to change rfid timer parameters on the fly
* RFID: Indala reading, card detection, card verification
* Rfid: indala writing
SG 4 лет назад
Родитель
Сommit
6926cf8b7e

+ 15 - 0
applications/lfrfid/helpers/decoder-gpio-out.cpp

@@ -0,0 +1,15 @@
+#include "decoder-gpio-out.h"
+#include <furi.h>
+#include <api-hal.h>
+
+void DecoderGpioOut::process_front(bool polarity, uint32_t time) {
+    hal_gpio_write(&gpio_ext_pa7, polarity);
+}
+
+DecoderGpioOut::DecoderGpioOut() {
+    hal_gpio_init_simple(&gpio_ext_pa7, GpioModeOutputPushPull);
+}
+
+DecoderGpioOut::~DecoderGpioOut() {
+    hal_gpio_init_simple(&gpio_ext_pa7, GpioModeAnalog);
+}

+ 14 - 0
applications/lfrfid/helpers/decoder-gpio-out.h

@@ -0,0 +1,14 @@
+#pragma once
+#include <stdint.h>
+#include <atomic>
+
+class DecoderGpioOut {
+public:
+    void process_front(bool polarity, uint32_t time);
+
+    DecoderGpioOut();
+    ~DecoderGpioOut();
+
+private:
+    void reset_state();
+};

+ 45 - 139
applications/lfrfid/helpers/decoder-indala.cpp

@@ -2,18 +2,24 @@
 #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;
+constexpr uint32_t us_per_bit = 255;
 
 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;
+        if(cursed_data_valid) {
+            indala.decode(
+                reinterpret_cast<const uint8_t*>(&cursed_raw_data),
+                sizeof(uint64_t),
+                data,
+                data_size);
+        } else {
+            indala.decode(
+                reinterpret_cast<const uint8_t*>(&raw_data), sizeof(uint64_t), data, data_size);
+        }
+        reset_state();
     }
 
     return result;
@@ -22,149 +28,49 @@ bool DecoderIndala::read(uint8_t* data, uint8_t data_size) {
 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;
+    process_internal(polarity, time, &raw_data);
+    if(ready) return;
 
-    if(sum_to_check % 2 == 0) {
-        if(id.checksum != 0b10) return;
+    if(polarity) {
+        time = time + 110;
     } else {
-        if(id.checksum != 0b01) return;
+        time = time - 110;
     }
 
-    // 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;
-        }
+    process_internal(!polarity, time, &cursed_raw_data);
+    if(ready) {
+        cursed_data_valid = true;
     }
+}
 
-    for(int8_t i = 0; i < 4; i--) {
-        if((number >> i) & 1) {
-            sum_to_check += 1;
-        }
-    }
+void DecoderIndala::process_internal(bool polarity, uint32_t time, uint64_t* data) {
+    time /= clocks_in_us;
+    time += (us_per_bit / 2);
 
-    if(id.p1) {
-        sum_to_check += 1;
-    }
+    uint32_t bit_count = (time / us_per_bit);
 
-    if((sum_to_check % 2) == 1) return;
+    if(bit_count < 64) {
+        for(uint32_t i = 0; i < bit_count; i++) {
+            *data = (*data << 1) | polarity;
 
-    // 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((*data >> 32) == 0xa0000000ULL) {
+                if(indala.can_be_decoded(
+                       reinterpret_cast<const uint8_t*>(data), sizeof(uint64_t))) {
+                    ready = true;
+                    break;
+                }
+            }
         }
     }
+}
 
-    if(id.p2) {
-        sum_to_check += 1;
-    }
-
-    if((sum_to_check % 2) != 1) return;
-
-    ready = true;
+DecoderIndala::DecoderIndala() {
+    reset_state();
 }
+
+void DecoderIndala::reset_state() {
+    raw_data = 0;
+    cursed_raw_data = 0;
+    ready = false;
+    cursed_data_valid = false;
+}

+ 7 - 10
applications/lfrfid/helpers/decoder-indala.h

@@ -2,27 +2,24 @@
 #include <stdint.h>
 #include <limits.h>
 #include <atomic>
+#include "protocols/protocol-indala-40134.h"
 
 class DecoderIndala {
 public:
     bool read(uint8_t* data, uint8_t data_size);
     void process_front(bool polarity, uint32_t time);
 
+    void process_internal(bool polarity, uint32_t time, uint64_t* data);
+
     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;
+    uint64_t raw_data;
+    uint64_t cursed_raw_data;
 
     std::atomic<bool> ready;
-    uint8_t facility = 0;
-    uint16_t number = 0;
+    std::atomic<bool> cursed_data_valid;
+    ProtocolIndala40134 indala;
 };

+ 111 - 5
applications/lfrfid/helpers/protocols/protocol-indala-40134.cpp

@@ -12,6 +12,11 @@ static void set_bit(bool bit, uint8_t position, Indala40134CardData* card_data)
     }
 }
 
+static bool get_bit(uint8_t position, const Indala40134CardData* card_data) {
+    position = (sizeof(Indala40134CardData) * 8) - 1 - position;
+    return (*card_data >> position) & 1;
+}
+
 uint8_t ProtocolIndala40134::get_encoded_data_size() {
     return sizeof(Indala40134CardData);
 }
@@ -110,6 +115,46 @@ void ProtocolIndala40134::encode(
     memcpy(encoded_data, &card_data, get_encoded_data_size());
 }
 
+// factory code
+static uint8_t get_fc(const Indala40134CardData* card_data) {
+    uint8_t fc = 0;
+
+    fc = fc << 1 | get_bit(57, card_data);
+    fc = fc << 1 | get_bit(49, card_data);
+    fc = fc << 1 | get_bit(44, card_data);
+    fc = fc << 1 | get_bit(47, card_data);
+    fc = fc << 1 | get_bit(48, card_data);
+    fc = fc << 1 | get_bit(53, card_data);
+    fc = fc << 1 | get_bit(39, card_data);
+    fc = fc << 1 | get_bit(58, card_data);
+
+    return fc;
+}
+
+// card number
+static uint16_t get_cn(const Indala40134CardData* card_data) {
+    uint16_t cn = 0;
+
+    cn = cn << 1 | get_bit(42, card_data);
+    cn = cn << 1 | get_bit(45, card_data);
+    cn = cn << 1 | get_bit(43, card_data);
+    cn = cn << 1 | get_bit(40, card_data);
+    cn = cn << 1 | get_bit(52, card_data);
+    cn = cn << 1 | get_bit(36, card_data);
+    cn = cn << 1 | get_bit(35, card_data);
+    cn = cn << 1 | get_bit(51, card_data);
+    cn = cn << 1 | get_bit(46, card_data);
+    cn = cn << 1 | get_bit(33, card_data);
+    cn = cn << 1 | get_bit(37, card_data);
+    cn = cn << 1 | get_bit(54, card_data);
+    cn = cn << 1 | get_bit(56, card_data);
+    cn = cn << 1 | get_bit(59, card_data);
+    cn = cn << 1 | get_bit(50, card_data);
+    cn = cn << 1 | get_bit(41, card_data);
+
+    return cn;
+}
+
 void ProtocolIndala40134::decode(
     const uint8_t* encoded_data,
     const uint8_t encoded_data_size,
@@ -117,15 +162,76 @@ void ProtocolIndala40134::decode(
     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);
+
+    const Indala40134CardData* card_data =
+        reinterpret_cast<const Indala40134CardData*>(encoded_data);
+
+    uint8_t fc = get_fc(card_data);
+    uint16_t card = get_cn(card_data);
+
+    decoded_data[0] = fc;
+    decoded_data[1] = card >> 8;
+    decoded_data[2] = card;
 }
 
 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;
+    bool can_be_decoded = false;
+
+    const Indala40134CardData* card_data =
+        reinterpret_cast<const Indala40134CardData*>(encoded_data);
+
+    do {
+        // preambula
+        if((*card_data >> 32) != 0xa0000000UL) break;
+
+        // data
+        const uint32_t fc_and_card = get_fc(card_data) << 16 | get_cn(card_data);
+
+        // checksum
+        const uint8_t checksum = get_bit(62, card_data) << 1 | get_bit(63, card_data);
+        uint8_t checksum_sum = 0;
+        checksum_sum += ((fc_and_card >> 14) & 1);
+        checksum_sum += ((fc_and_card >> 12) & 1);
+        checksum_sum += ((fc_and_card >> 9) & 1);
+        checksum_sum += ((fc_and_card >> 8) & 1);
+        checksum_sum += ((fc_and_card >> 6) & 1);
+        checksum_sum += ((fc_and_card >> 5) & 1);
+        checksum_sum += ((fc_and_card >> 2) & 1);
+        checksum_sum += ((fc_and_card >> 0) & 1);
+        checksum_sum = checksum_sum & 0b1;
+
+        if(checksum_sum == 1 && checksum == 0b01) {
+        } else if(checksum_sum == 0 && checksum == 0b10) {
+        } else {
+            break;
+        }
+
+        // wiegand parity bits
+        // even parity sum calculation (high 12 bits of data)
+        const bool even_parity = get_bit(34, card_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++;
+            }
+        }
+        if(even_parity_sum % 2 != even_parity) break;
+
+        // odd parity sum calculation (low 12 bits of data)
+        const bool odd_parity = get_bit(38, card_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++;
+            }
+        }
+        if(odd_parity_sum % 2 != odd_parity) break;
+
+        can_be_decoded = true;
+    } while(false);
+
+    return can_be_decoded;
 }

+ 106 - 45
applications/lfrfid/helpers/rfid-reader.cpp

@@ -17,19 +17,47 @@ struct RfidReaderAccessor {
 
 void RfidReader::decode(bool polarity) {
     uint32_t current_dwt_value = DWT->CYCCNT;
+    uint32_t period = current_dwt_value - last_dwt_value;
+    last_dwt_value = current_dwt_value;
 
+    //decoder_gpio_out.process_front(polarity, period);
     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);
+        decoder_em.process_front(polarity, period);
+        decoder_hid26.process_front(polarity, period);
+        break;
+    case Type::Indala:
+        decoder_em.process_front(polarity, period);
+        decoder_hid26.process_front(polarity, period);
+        decoder_indala.process_front(polarity, period);
+        break;
+    }
+
+    detect_ticks++;
+}
+
+bool RfidReader::switch_timer_elapsed() {
+    const uint32_t seconds_to_switch = osKernelGetTickFreq() * 2.0f;
+    return (osKernelGetTickCount() - switch_os_tick_last) > seconds_to_switch;
+}
+
+void RfidReader::switch_timer_reset() {
+    switch_os_tick_last = osKernelGetTickCount();
+}
 
-        last_dwt_value = current_dwt_value;
+void RfidReader::switch_mode() {
+    switch(type) {
+    case Type::Normal:
+        type = Type::Indala;
+        api_hal_rfid_change_read_config(62500.0f, 0.25f);
         break;
     case Type::Indala:
+        type = Type::Normal;
+        api_hal_rfid_change_read_config(125000.0f, 0.5f);
         break;
     }
+
+    switch_timer_reset();
 }
 
 static void comparator_trigger_callback(void* hcomp, void* comp_ctx) {
@@ -45,47 +73,103 @@ static void comparator_trigger_callback(void* hcomp, void* comp_ctx) {
 RfidReader::RfidReader() {
 }
 
-void RfidReader::start(Type _type) {
-    type = _type;
+void RfidReader::start() {
+    type = Type::Normal;
 
-    start_gpio();
+    api_hal_rfid_pins_read();
+    api_hal_rfid_tim_read(125000, 0.5);
+    api_hal_rfid_tim_read_start();
+    start_comparator();
+
+    switch_timer_reset();
+    last_readed_count = 0;
+}
+
+void RfidReader::start_forced(RfidReader::Type _type) {
+    type = _type;
     switch(type) {
     case Type::Normal:
-        start_timer();
+        start();
         break;
     case Type::Indala:
-        start_timer_indala();
+        api_hal_rfid_pins_read();
+        api_hal_rfid_tim_read(62500.0f, 0.25f);
+        api_hal_rfid_tim_read_start();
+        start_comparator();
+
+        switch_timer_reset();
+        last_readed_count = 0;
         break;
     }
-
-    start_comparator();
 }
 
 void RfidReader::stop() {
-    stop_gpio();
-    stop_timer();
+    api_hal_rfid_pins_reset();
+    api_hal_rfid_tim_read_stop();
+    api_hal_rfid_tim_reset();
     stop_comparator();
 }
 
-bool RfidReader::read(LfrfidKeyType* type, uint8_t* data, uint8_t data_size) {
+bool RfidReader::read(LfrfidKeyType* _type, uint8_t* data, uint8_t data_size) {
     bool result = false;
+    bool something_readed = false;
 
+    // reading
     if(decoder_em.read(data, data_size)) {
-        *type = LfrfidKeyType::KeyEM4100;
-        result = true;
+        *_type = LfrfidKeyType::KeyEM4100;
+        something_readed = true;
     }
 
     if(decoder_hid26.read(data, data_size)) {
-        *type = LfrfidKeyType::KeyH10301;
-        result = true;
+        *_type = LfrfidKeyType::KeyH10301;
+        something_readed = true;
     }
 
-    //decoder_indala.read(NULL, 0);
-    //decoder_analyzer.read(NULL, 0);
+    if(decoder_indala.read(data, data_size)) {
+        *_type = LfrfidKeyType::KeyI40134;
+        something_readed = true;
+    }
+
+    // validation
+    if(something_readed) {
+        switch_timer_reset();
+
+        if(last_readed_type == *_type && memcmp(last_readed_data, data, data_size) == 0) {
+            last_readed_count = last_readed_count + 1;
+
+            if(last_readed_count > 2) {
+                result = true;
+            }
+        } else {
+            last_readed_type = *_type;
+            memcpy(last_readed_data, data, data_size);
+            last_readed_count = 0;
+        }
+    }
+
+    // mode switching
+    if(switch_timer_elapsed()) {
+        switch_mode();
+        last_readed_count = 0;
+    }
 
     return result;
 }
 
+bool RfidReader::detect() {
+    bool detected = false;
+    if(detect_ticks > 10) {
+        detected = true;
+    }
+    detect_ticks = 0;
+
+    return detected;
+}
+
+bool RfidReader::any_read() {
+    return last_readed_count > 0;
+}
+
 void RfidReader::start_comparator(void) {
     api_interrupt_add(comparator_trigger_callback, InterruptTypeComparatorTrigger, this);
     last_dwt_value = DWT->CYCCNT;
@@ -93,7 +177,7 @@ void RfidReader::start_comparator(void) {
     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.Hysteresis = COMP_HYSTERESIS_HIGH;
     hcomp1.Init.BlankingSrce = COMP_BLANKINGSRC_NONE;
     hcomp1.Init.Mode = COMP_POWERMODE_MEDIUMSPEED;
     hcomp1.Init.WindowMode = COMP_WINDOWMODE_DISABLE;
@@ -105,30 +189,7 @@ void RfidReader::start_comparator(void) {
     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();
 }

+ 19 - 7
applications/lfrfid/helpers/rfid-reader.h

@@ -1,5 +1,6 @@
 #pragma once
-#include "decoder-analyzer.h"
+//#include "decoder-analyzer.h"
+#include "decoder-gpio-out.h"
 #include "decoder-emmarine.h"
 #include "decoder-hid26.h"
 #include "decoder-indala.h"
@@ -13,14 +14,19 @@ public:
     };
 
     RfidReader();
-    void start(Type type);
+    void start();
+    void start_forced(RfidReader::Type type);
     void stop();
     bool read(LfrfidKeyType* type, uint8_t* data, uint8_t data_size);
 
+    bool detect();
+    bool any_read();
+
 private:
     friend struct RfidReaderAccessor;
 
     //DecoderAnalyzer decoder_analyzer;
+    //DecoderGpioOut decoder_gpio_out;
     DecoderEMMarine decoder_em;
     DecoderHID26 decoder_hid26;
     DecoderIndala decoder_indala;
@@ -28,14 +34,20 @@ private:
     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);
 
+    uint32_t detect_ticks;
+
+    uint32_t switch_os_tick_last;
+    bool switch_timer_elapsed();
+    void switch_timer_reset();
+    void switch_mode();
+
+    LfrfidKeyType last_readed_type;
+    uint8_t last_readed_data[LFRFID_KEY_SIZE];
+    uint8_t last_readed_count;
+
     Type type = Type::Normal;
 };

+ 40 - 21
applications/lfrfid/helpers/rfid-worker.cpp

@@ -7,7 +7,7 @@ RfidWorker::~RfidWorker() {
 }
 
 void RfidWorker::start_read() {
-    reader.start(RfidReader::Type::Normal);
+    reader.start();
 }
 
 bool RfidWorker::read() {
@@ -25,6 +25,14 @@ bool RfidWorker::read() {
     return result;
 }
 
+bool RfidWorker::detect() {
+    return reader.detect();
+}
+
+bool RfidWorker::any_read() {
+    return reader.any_read();
+}
+
 void RfidWorker::stop_read() {
     reader.stop();
 }
@@ -36,7 +44,7 @@ void RfidWorker::start_write() {
 
     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(30, std::bind(&RfidWorker::sq_write_validate, this));
     write_sequence->do_every_tick(1, std::bind(&RfidWorker::sq_write_stop_validate, this));
 }
 
@@ -59,28 +67,39 @@ void RfidWorker::stop_emulate() {
 }
 
 void RfidWorker::sq_write() {
-    // TODO expand this
+    for(size_t i = 0; i < 5; i++) {
+        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;
+        case LfrfidKeyType::KeyI40134:
+            writer.start();
+            writer.write_indala(key.get_data());
+            writer.stop();
+            break;
+        }
+    }
+}
+
+void RfidWorker::sq_write_start_validate() {
     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();
+        reader.start_forced(RfidReader::Type::Normal);
         break;
-
-    default:
+    case LfrfidKeyType::KeyI40134:
+        reader.start_forced(RfidReader::Type::Indala);
         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};
@@ -88,7 +107,11 @@ void RfidWorker::sq_write_validate() {
 
     bool result = reader.read(&type, data, data_size);
 
-    if(result) {
+    if(result && (write_result != WriteResult::Ok)) {
+        if(validate_counts > (5 * 60)) {
+            write_result = WriteResult::NotWritable;
+        }
+
         if(type == key.get_type()) {
             if(memcmp(data, key.get_data(), key.get_type_data_count()) == 0) {
                 write_result = WriteResult::Ok;
@@ -99,10 +122,6 @@ void RfidWorker::sq_write_validate() {
         } else {
             validate_counts++;
         }
-
-        if(validate_counts > 5) {
-            write_result = WriteResult::NotWritable;
-        }
     };
 }
 

+ 2 - 0
applications/lfrfid/helpers/rfid-worker.h

@@ -13,6 +13,8 @@ public:
 
     void start_read();
     bool read();
+    bool detect();
+    bool any_read();
     void stop_read();
 
     enum class WriteResult : uint8_t {

+ 18 - 1
applications/lfrfid/helpers/rfid-writer.cpp

@@ -2,6 +2,7 @@
 #include <api-hal.h>
 #include "protocols/protocol-emmarin.h"
 #include "protocols/protocol-hid-h10301.h"
+#include "protocols/protocol-indala-40134.h"
 
 extern COMP_HandleTypeDef hcomp1;
 
@@ -115,7 +116,7 @@ void RfidWriter::write_em(const 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;
+    const uint32_t em_config_block_data = 0b00000000000101001000000001000000;
 
     __disable_irq();
     write_block(0, 0, false, em_config_block_data);
@@ -140,3 +141,19 @@ void RfidWriter::write_hid(const uint8_t hid_data[3]) {
     write_reset();
     __enable_irq();
 }
+
+void RfidWriter::write_indala(const uint8_t indala_data[3]) {
+    ProtocolIndala40134 indala_card;
+    uint32_t card_data[2];
+    indala_card.encode(
+        indala_data, 3, reinterpret_cast<uint8_t*>(&card_data), sizeof(card_data) * 2);
+
+    const uint32_t indala_config_block_data = 0b00000000000010000001000001000000;
+
+    __disable_irq();
+    write_block(0, 0, false, indala_config_block_data);
+    write_block(0, 1, false, card_data[0]);
+    write_block(0, 2, false, card_data[1]);
+    write_reset();
+    __enable_irq();
+}

+ 1 - 0
applications/lfrfid/helpers/rfid-writer.h

@@ -9,6 +9,7 @@ public:
     void stop();
     void write_em(const uint8_t em_data[5]);
     void write_hid(const uint8_t hid_data[3]);
+    void write_indala(const uint8_t indala_data[3]);
 
 private:
     void write_gap(uint32_t gap_time);

+ 1 - 1
applications/lfrfid/lfrfid-cli.cpp

@@ -46,7 +46,7 @@ bool lfrfid_cli_get_key_type(string_t data, LfrfidKeyType* type) {
 
 void lfrfid_cli_read(Cli* cli) {
     RfidReader reader;
-    reader.start(RfidReader::Type::Normal);
+    reader.start();
 
     static const uint8_t data_size = LFRFID_KEY_SIZE;
     uint8_t data[data_size] = {0};

+ 1 - 3
applications/lfrfid/scene/lfrfid-app-scene-read-success.cpp

@@ -55,6 +55,7 @@ void LfRfidAppSceneReadSuccess::on_enter(LfRfidApp* app, bool need_restore) {
             string_get_cstr(string[2]), 68, 47, AlignLeft, AlignBottom, FontSecondary);
         break;
     case LfrfidKeyType::KeyH10301:
+    case LfrfidKeyType::KeyI40134:
         line_1_text->set_text("HEX:", 65, 23, AlignRight, AlignBottom, FontSecondary);
         line_2_text->set_text("FC:", 65, 35, AlignRight, AlignBottom, FontSecondary);
         line_3_text->set_text("Card:", 65, 47, AlignRight, AlignBottom, FontSecondary);
@@ -73,9 +74,6 @@ void LfRfidAppSceneReadSuccess::on_enter(LfRfidApp* app, bool need_restore) {
         line_3_value->set_text(
             string_get_cstr(string[2]), 68, 47, AlignLeft, AlignBottom, FontSecondary);
         break;
-    case LfrfidKeyType::KeyI40134:
-        //TODO implement when we can read Indala
-        break;
     }
 
     app->view_controller.switch_to<ContainerVM>();

+ 7 - 1
applications/lfrfid/scene/lfrfid-app-scene-read.cpp

@@ -18,7 +18,13 @@ bool LfRfidAppSceneRead::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
             notification_message(app->notification, &sequence_success);
             app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::ReadSuccess);
         } else {
-            notification_message(app->notification, &sequence_blink_red_10);
+            if(app->worker.any_read()) {
+                notification_message(app->notification, &sequence_blink_green_10);
+            } else if(app->worker.detect()) {
+                notification_message(app->notification, &sequence_blink_blue_10);
+            } else {
+                notification_message(app->notification, &sequence_blink_red_10);
+            }
         }
     }
 

+ 7 - 0
firmware/targets/api-hal-include/api-hal-rfid.h

@@ -105,6 +105,13 @@ void api_hal_rfid_set_read_period(uint32_t period);
  */
 void api_hal_rfid_set_read_pulse(uint32_t pulse);
 
+/**
+ * Сhanges the configuration of the RFID timer "on a fly"
+ * @param freq new frequency
+ * @param duty_cycle new duty cycle
+ */
+void api_hal_rfid_change_read_config(float freq, float duty_cycle);
+
 #ifdef __cplusplus
 }
 #endif

+ 6 - 0
firmware/targets/f6/api-hal/api-hal-rfid.c

@@ -269,4 +269,10 @@ void api_hal_rfid_set_read_pulse(uint32_t pulse) {
         furi_check(0);
         break;
     }
+}
+
+void api_hal_rfid_change_read_config(float freq, float duty_cycle) {
+    uint32_t period = (uint32_t)((SystemCoreClock) / freq) - 1;
+    api_hal_rfid_set_read_period(period);
+    api_hal_rfid_set_read_pulse(period * duty_cycle);
 }