FL-549 Reading iButton "Cyfral" by HW comparator (#273)
* new comparator-based reader * working comparator reader * add keys "integrity" check * reset events queue
This commit is contained in:
		
							parent
							
								
									8b6e7fd4ae
								
							
						
					
					
						commit
						b32c5f3897
					
				| @ -1,12 +1,12 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include "ibutton.h" | #include "ibutton.h" | ||||||
| #include "cyfral_reader.h" | #include "cyfral_reader_comp.h" | ||||||
| 
 | 
 | ||||||
| class AppiButtonModeCyfralRead : public AppTemplateMode<AppiButtonState, AppiButtonEvent> { | class AppiButtonModeCyfralRead : public AppTemplateMode<AppiButtonState, AppiButtonEvent> { | ||||||
| public: | public: | ||||||
|     const char* name = "cyfral read"; |     const char* name = "cyfral read"; | ||||||
|     AppiButton* app; |     AppiButton* app; | ||||||
|     CyfralReader* reader; |     CyfralReaderComp* reader; | ||||||
| 
 | 
 | ||||||
|     void event(AppiButtonEvent* event, AppiButtonState* state); |     void event(AppiButtonEvent* event, AppiButtonState* state); | ||||||
|     void render(Canvas* canvas, AppiButtonState* state); |     void render(Canvas* canvas, AppiButtonState* state); | ||||||
| @ -15,16 +15,48 @@ public: | |||||||
| 
 | 
 | ||||||
|     AppiButtonModeCyfralRead(AppiButton* parent_app) { |     AppiButtonModeCyfralRead(AppiButton* parent_app) { | ||||||
|         app = parent_app; |         app = parent_app; | ||||||
|         reader = new CyfralReader(ADC1, ADC_CHANNEL_14); | 
 | ||||||
|  |         // TODO open record
 | ||||||
|  |         const GpioPin* one_wire_pin_record = &ibutton_gpio; | ||||||
|  |         reader = new CyfralReaderComp(one_wire_pin_record); | ||||||
|     }; |     }; | ||||||
|  | 
 | ||||||
|  |     static const uint8_t key_length = 4; | ||||||
|  |     static const uint8_t num_keys_to_check = 4; | ||||||
|  |     uint8_t key_index = 0; | ||||||
|  |     uint8_t keys[num_keys_to_check][4]; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void AppiButtonModeCyfralRead::event(AppiButtonEvent* event, AppiButtonState* state) { | void AppiButtonModeCyfralRead::event(AppiButtonEvent* event, AppiButtonState* state) { | ||||||
|     if(event->type == AppiButtonEvent::EventTypeTick) { |     if(event->type == AppiButtonEvent::EventTypeTick) { | ||||||
|         uint8_t data[8]; |         // if we read a key
 | ||||||
|         if(reader->read(data, 4)) { |         if(reader->read(keys[key_index], key_length)) { | ||||||
|             memcpy(app->state.cyfral_address[app->state.cyfral_address_index], data, 4); |             // read next key
 | ||||||
|             app->blink_green(); |             key_index++; | ||||||
|  | 
 | ||||||
|  |             // if we read sufficient amount of keys
 | ||||||
|  |             if(key_index >= num_keys_to_check) { | ||||||
|  |                 bool result = true; | ||||||
|  |                 key_index = 0; | ||||||
|  | 
 | ||||||
|  |                 // compare all keys
 | ||||||
|  |                 for(uint8_t i = 1; i < num_keys_to_check; i++) { | ||||||
|  |                     if(memcmp(keys[i], keys[i - 1], key_length) != 0) { | ||||||
|  |                         result = false; | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 // if all keys is same
 | ||||||
|  |                 if(result) { | ||||||
|  |                     // copy key to mem and blink
 | ||||||
|  |                     memcpy( | ||||||
|  |                         app->state.cyfral_address[app->state.cyfral_address_index], | ||||||
|  |                         keys[key_index], | ||||||
|  |                         key_length); | ||||||
|  |                     app->blink_green(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } else if(event->type == AppiButtonEvent::EventTypeKey) { |     } else if(event->type == AppiButtonEvent::EventTypeKey) { | ||||||
|         if(event->value.input.state && event->value.input.input == InputUp) { |         if(event->value.input.state && event->value.input.input == InputUp) { | ||||||
|  | |||||||
							
								
								
									
										283
									
								
								lib/cyfral/cyfral_reader_comp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										283
									
								
								lib/cyfral/cyfral_reader_comp.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,283 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "flipper.h" | ||||||
|  | #include "flipper_v2.h" | ||||||
|  | #include "callback-connector.h" | ||||||
|  | #include <atomic> | ||||||
|  | 
 | ||||||
|  | enum class CyfralReaderCompError : uint8_t { | ||||||
|  |     NO_ERROR = 0, | ||||||
|  |     UNABLE_TO_DETECT = 1, | ||||||
|  |     RAW_DATA_SIZE_ERROR = 2, | ||||||
|  |     UNKNOWN_NIBBLE_VALUE = 3, | ||||||
|  |     NO_START_NIBBLE = 4, | ||||||
|  |     NOT_ENOUGH_DATA = 5, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | extern COMP_HandleTypeDef hcomp1; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     bool value; | ||||||
|  |     uint32_t dwt_value; | ||||||
|  | } CompEvent; | ||||||
|  | 
 | ||||||
|  | class CyfralReaderComp { | ||||||
|  | private: | ||||||
|  |     bool capture_data(bool* data, uint16_t capture_size); | ||||||
|  |     bool parse_data(bool* raw_data, uint16_t capture_size, uint8_t* data, uint8_t count); | ||||||
|  |     uint32_t search_array_in_array( | ||||||
|  |         const bool* haystack, | ||||||
|  |         const uint32_t haystack_size, | ||||||
|  |         const bool* needle, | ||||||
|  |         const uint32_t needle_size); | ||||||
|  | 
 | ||||||
|  |     // key is 9 nibbles
 | ||||||
|  |     static const uint16_t bits_in_nibble = 4; | ||||||
|  |     static const uint16_t key_length = 9; | ||||||
|  |     static const uint32_t capture_size = key_length * bits_in_nibble * 2; | ||||||
|  |     CyfralReaderCompError error; | ||||||
|  |     const GpioPin* pin_record; | ||||||
|  | 
 | ||||||
|  |     std::atomic<bool> ready_to_process; | ||||||
|  |     void comparator_trigger_callback(void* hcomp, void* comp_ctx); | ||||||
|  |     osMessageQueueId_t comp_event_queue; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     CyfralReaderComp(const GpioPin* emulate_pin); | ||||||
|  |     ~CyfralReaderComp(); | ||||||
|  |     void start(void); | ||||||
|  |     void stop(void); | ||||||
|  |     bool read(uint8_t* data, uint8_t count); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | bool CyfralReaderComp::capture_data(bool* data, uint16_t capture_size) { | ||||||
|  |     uint32_t prev_timing = 0; | ||||||
|  |     uint16_t data_index = 0; | ||||||
|  |     CompEvent event_0, event_1; | ||||||
|  |     osStatus_t status; | ||||||
|  | 
 | ||||||
|  |     // read first event to get initial timing
 | ||||||
|  |     status = osMessageQueueGet(comp_event_queue, &event_0, NULL, 0); | ||||||
|  | 
 | ||||||
|  |     if(status != osOK) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     prev_timing = event_0.dwt_value; | ||||||
|  | 
 | ||||||
|  |     // read second event until we get 0
 | ||||||
|  |     while(1) { | ||||||
|  |         status = osMessageQueueGet(comp_event_queue, &event_0, NULL, 0); | ||||||
|  |         if(status != osOK) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         prev_timing = event_0.dwt_value; | ||||||
|  |         if(event_0.value == 0) break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     while(1) { | ||||||
|  |         // if event "zero" correct
 | ||||||
|  |         if(status == osOK && event_0.value == 0) { | ||||||
|  |             // get timing
 | ||||||
|  |             event_0.dwt_value -= prev_timing; | ||||||
|  |             prev_timing += event_0.dwt_value; | ||||||
|  | 
 | ||||||
|  |             // read next event
 | ||||||
|  |             status = osMessageQueueGet(comp_event_queue, &event_1, NULL, 0); | ||||||
|  | 
 | ||||||
|  |             // if event "one" correct
 | ||||||
|  |             if(status == osOK && event_1.value == 1) { | ||||||
|  |                 // get timing
 | ||||||
|  |                 event_1.dwt_value -= prev_timing; | ||||||
|  |                 prev_timing += event_1.dwt_value; | ||||||
|  | 
 | ||||||
|  |                 // calculate percentage of event "one" to full timing
 | ||||||
|  |                 uint32_t full_timing = event_0.dwt_value + event_1.dwt_value; | ||||||
|  |                 uint32_t percentage_1 = 1000000 / full_timing * event_1.dwt_value; | ||||||
|  | 
 | ||||||
|  |                 // write captured data
 | ||||||
|  |                 data[data_index] = percentage_1 > 500000 ? 0 : 1; | ||||||
|  |                 data_index++; | ||||||
|  |                 if(data_index >= capture_size) return true; | ||||||
|  | 
 | ||||||
|  |                 status = osMessageQueueGet(comp_event_queue, &event_0, NULL, 0); | ||||||
|  |             } else { | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     osMessageQueueReset(comp_event_queue); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint32_t CyfralReaderComp::search_array_in_array( | ||||||
|  |     const bool* haystack, | ||||||
|  |     const uint32_t haystack_size, | ||||||
|  |     const bool* needle, | ||||||
|  |     const uint32_t needle_size) { | ||||||
|  |     uint32_t haystack_index = 0, needle_index = 0; | ||||||
|  | 
 | ||||||
|  |     while(haystack_index < haystack_size && needle_index < needle_size) { | ||||||
|  |         if(haystack[haystack_index] == needle[needle_index]) { | ||||||
|  |             haystack_index++; | ||||||
|  |             needle_index++; | ||||||
|  |             if(needle_index == needle_size) { | ||||||
|  |                 return (haystack_index - needle_size); | ||||||
|  |             }; | ||||||
|  |         } else { | ||||||
|  |             haystack_index = haystack_index - needle_index + 1; | ||||||
|  |             needle_index = 0; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return haystack_index; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CyfralReaderComp::comparator_trigger_callback(void* hcomp, void* comp_ctx) { | ||||||
|  |     CyfralReaderComp* _this = static_cast<CyfralReaderComp*>(comp_ctx); | ||||||
|  |     COMP_HandleTypeDef* _hcomp = static_cast<COMP_HandleTypeDef*>(hcomp); | ||||||
|  | 
 | ||||||
|  |     // check that hw is comparator 1
 | ||||||
|  |     if(_hcomp != &hcomp1) return; | ||||||
|  | 
 | ||||||
|  |     // if queue if not full
 | ||||||
|  |     if(_this->ready_to_process == false) { | ||||||
|  |         // send event to queue
 | ||||||
|  |         CompEvent event; | ||||||
|  |         event.value = (HAL_COMP_GetOutputLevel(_hcomp) == COMP_OUTPUT_LEVEL_HIGH); | ||||||
|  |         event.dwt_value = DWT->CYCCNT; | ||||||
|  |         osStatus_t status = osMessageQueuePut(_this->comp_event_queue, &event, 0, 0); | ||||||
|  | 
 | ||||||
|  |         // queue is full, so we need to process data
 | ||||||
|  |         if(status != osOK) { | ||||||
|  |             _this->ready_to_process = true; | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool CyfralReaderComp::parse_data( | ||||||
|  |     bool* raw_data, | ||||||
|  |     uint16_t capture_size, | ||||||
|  |     uint8_t* data, | ||||||
|  |     uint8_t count) { | ||||||
|  |     const bool start_nibble[bits_in_nibble] = {1, 1, 1, 0}; | ||||||
|  |     uint32_t start_position = | ||||||
|  |         search_array_in_array(raw_data, capture_size, start_nibble, bits_in_nibble); | ||||||
|  |     uint32_t end_position = 0; | ||||||
|  | 
 | ||||||
|  |     memset(data, 0, count); | ||||||
|  | 
 | ||||||
|  |     if(start_position < capture_size) { | ||||||
|  |         start_position = start_position + bits_in_nibble; | ||||||
|  |         end_position = start_position + count * 2 * bits_in_nibble; | ||||||
|  | 
 | ||||||
|  |         if(end_position >= capture_size) { | ||||||
|  |             error = CyfralReaderCompError::RAW_DATA_SIZE_ERROR; | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         bool first_nibble = true; | ||||||
|  |         uint8_t data_position = 0; | ||||||
|  |         uint8_t nibble_value = 0; | ||||||
|  | 
 | ||||||
|  |         while(data_position < count) { | ||||||
|  |             nibble_value = !raw_data[start_position] << 3 | !raw_data[start_position + 1] << 2 | | ||||||
|  |                            !raw_data[start_position + 2] << 1 | !raw_data[start_position + 3]; | ||||||
|  | 
 | ||||||
|  |             switch(nibble_value) { | ||||||
|  |             case(0x7): | ||||||
|  |             case(0xB): | ||||||
|  |             case(0xD): | ||||||
|  |             case(0xE): | ||||||
|  |                 break; | ||||||
|  |             default: | ||||||
|  |                 error = CyfralReaderCompError::UNKNOWN_NIBBLE_VALUE; | ||||||
|  |                 return false; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if(first_nibble) { | ||||||
|  |                 data[data_position] |= nibble_value << 4; | ||||||
|  |             } else { | ||||||
|  |                 data[data_position] |= nibble_value; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             first_nibble = !first_nibble; | ||||||
|  | 
 | ||||||
|  |             if(first_nibble) { | ||||||
|  |                 data_position++; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             start_position = start_position + bits_in_nibble; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         error = CyfralReaderCompError::NO_ERROR; | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     error = CyfralReaderCompError::NO_START_NIBBLE; | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | CyfralReaderComp::CyfralReaderComp(const GpioPin* gpio_pin) { | ||||||
|  |     pin_record = gpio_pin; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | CyfralReaderComp::~CyfralReaderComp() { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CyfralReaderComp::start(void) { | ||||||
|  |     // pulldown lf-rfid pins to prevent interference
 | ||||||
|  |     // TODO open record
 | ||||||
|  |     GpioPin rfid_pull_pin = {.port = RFID_PULL_GPIO_Port, .pin = RFID_PULL_Pin}; | ||||||
|  |     gpio_init((GpioPin*)&rfid_pull_pin, GpioModeOutputOpenDrain); | ||||||
|  |     gpio_write((GpioPin*)&rfid_pull_pin, false); | ||||||
|  | 
 | ||||||
|  |     // TODO open record
 | ||||||
|  |     GpioPin rfid_out_pin = {.port = RFID_OUT_GPIO_Port, .pin = RFID_OUT_Pin}; | ||||||
|  |     gpio_init((GpioPin*)&rfid_out_pin, GpioModeOutputOpenDrain); | ||||||
|  |     gpio_write((GpioPin*)&rfid_out_pin, false); | ||||||
|  | 
 | ||||||
|  |     // connect comparator callback
 | ||||||
|  |     void* comp_ctx = this; | ||||||
|  |     comp_event_queue = osMessageQueueNew(capture_size * 2 + 2, sizeof(CompEvent), NULL); | ||||||
|  |     ready_to_process = false; | ||||||
|  | 
 | ||||||
|  |     auto cmp_cb = cbc::obtain_connector(this, &CyfralReaderComp::comparator_trigger_callback); | ||||||
|  |     api_interrupt_add(cmp_cb, InterruptTypeComparatorTrigger, comp_ctx); | ||||||
|  | 
 | ||||||
|  |     // start comaparator
 | ||||||
|  |     HAL_COMP_Start(&hcomp1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CyfralReaderComp::stop(void) { | ||||||
|  |     // stop comaparator
 | ||||||
|  |     HAL_COMP_Stop(&hcomp1); | ||||||
|  | 
 | ||||||
|  |     // disconnect comparator callback
 | ||||||
|  |     auto cmp_cb = cbc::obtain_connector(this, &CyfralReaderComp::comparator_trigger_callback); | ||||||
|  |     api_interrupt_remove(cmp_cb); | ||||||
|  |     osMessageQueueDelete(comp_event_queue); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool CyfralReaderComp::read(uint8_t* data, uint8_t count) { | ||||||
|  |     bool raw_data[capture_size]; | ||||||
|  |     bool result = false; | ||||||
|  |     error = CyfralReaderCompError::NO_ERROR; | ||||||
|  | 
 | ||||||
|  |     if(ready_to_process == false) { | ||||||
|  |         error = CyfralReaderCompError::NOT_ENOUGH_DATA; | ||||||
|  |     } else { | ||||||
|  |         memset(raw_data, 0, sizeof(bool) * capture_size); | ||||||
|  |         if(capture_data(raw_data, capture_size)) { | ||||||
|  |             if(parse_data(raw_data, capture_size, data, count)) { | ||||||
|  |                 result = true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         ready_to_process = false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 DrZlo13
						DrZlo13