[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
This commit is contained in:
		
							parent
							
								
									fb80f9537f
								
							
						
					
					
						commit
						6926cf8b7e
					
				
							
								
								
									
										15
									
								
								applications/lfrfid/helpers/decoder-gpio-out.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								applications/lfrfid/helpers/decoder-gpio-out.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -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
									
								
								applications/lfrfid/helpers/decoder-gpio-out.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								applications/lfrfid/helpers/decoder-gpio-out.h
									
									
									
									
									
										Normal file
									
								
							| @ -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(); | ||||
| }; | ||||
| @ -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++; | ||||
|     process_internal(polarity, time, &raw_data); | ||||
|     if(ready) return; | ||||
| 
 | ||||
|         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(); | ||||
|     if(polarity) { | ||||
|         time = time + 110; | ||||
|     } else { | ||||
|         time = time - 110; | ||||
|     } | ||||
| 
 | ||||
|     process_internal(!polarity, time, &cursed_raw_data); | ||||
|     if(ready) { | ||||
|         cursed_data_valid = true; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void DecoderIndala::process_internal(bool polarity, uint32_t time, uint64_t* data) { | ||||
|     time /= clocks_in_us; | ||||
|     time += (us_per_bit / 2); | ||||
| 
 | ||||
|     uint32_t bit_count = (time / us_per_bit); | ||||
| 
 | ||||
|     if(bit_count < 64) { | ||||
|         for(uint32_t i = 0; i < bit_count; i++) { | ||||
|             *data = (*data << 1) | polarity; | ||||
| 
 | ||||
|             if((*data >> 32) == 0xa0000000ULL) { | ||||
|                 if(indala.can_be_decoded( | ||||
|                        reinterpret_cast<const uint8_t*>(data), sizeof(uint64_t))) { | ||||
|                     ready = true; | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| DecoderIndala::DecoderIndala() { | ||||
|     reset_state(); | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
|     raw_data = 0; | ||||
|     cursed_raw_data = 0; | ||||
|     ready = false; | ||||
|     cursed_data_valid = false; | ||||
| } | ||||
| @ -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; | ||||
| }; | ||||
| @ -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; | ||||
| } | ||||
| @ -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);
 | ||||
| 
 | ||||
|         last_dwt_value = current_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(); | ||||
| } | ||||
| 
 | ||||
| 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(); | ||||
| } | ||||
| @ -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; | ||||
| }; | ||||
|  | ||||
| @ -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,7 +67,7 @@ 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(); | ||||
| @ -71,14 +79,25 @@ void RfidWorker::sq_write() { | ||||
|             writer.write_hid(key.get_data()); | ||||
|             writer.stop(); | ||||
|             break; | ||||
| 
 | ||||
|     default: | ||||
|         case LfrfidKeyType::KeyI40134: | ||||
|             writer.start(); | ||||
|             writer.write_indala(key.get_data()); | ||||
|             writer.stop(); | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void RfidWorker::sq_write_start_validate() { | ||||
|     reader.start(RfidReader::Type::Normal); | ||||
|     switch(key.get_type()) { | ||||
|     case LfrfidKeyType::KeyEM4100: | ||||
|     case LfrfidKeyType::KeyH10301: | ||||
|         reader.start_forced(RfidReader::Type::Normal); | ||||
|         break; | ||||
|     case LfrfidKeyType::KeyI40134: | ||||
|         reader.start_forced(RfidReader::Type::Indala); | ||||
|         break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void RfidWorker::sq_write_validate() { | ||||
| @ -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; | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -13,6 +13,8 @@ public: | ||||
| 
 | ||||
|     void start_read(); | ||||
|     bool read(); | ||||
|     bool detect(); | ||||
|     bool any_read(); | ||||
|     void stop_read(); | ||||
| 
 | ||||
|     enum class WriteResult : uint8_t { | ||||
|  | ||||
| @ -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(); | ||||
| } | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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}; | ||||
|  | ||||
| @ -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>(); | ||||
|  | ||||
| @ -17,10 +17,16 @@ bool LfRfidAppSceneRead::on_event(LfRfidApp* app, LfRfidApp::Event* event) { | ||||
|         if(app->worker.read()) { | ||||
|             notification_message(app->notification, &sequence_success); | ||||
|             app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::ReadSuccess); | ||||
|         } else { | ||||
|             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); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
|  | ||||
| @ -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 | ||||
| @ -270,3 +270,9 @@ void api_hal_rfid_set_read_pulse(uint32_t pulse) { | ||||
|         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); | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 SG
						SG