diff --git a/applications/ibutton/ibutton_mode_cyfral_read.h b/applications/ibutton/ibutton_mode_cyfral_read.h index 828662830..a887289cf 100644 --- a/applications/ibutton/ibutton_mode_cyfral_read.h +++ b/applications/ibutton/ibutton_mode_cyfral_read.h @@ -1,12 +1,12 @@ #pragma once #include "ibutton.h" -#include "cyfral_reader.h" +#include "cyfral_reader_comp.h" class AppiButtonModeCyfralRead : public AppTemplateMode { public: const char* name = "cyfral read"; AppiButton* app; - CyfralReader* reader; + CyfralReaderComp* reader; void event(AppiButtonEvent* event, AppiButtonState* state); void render(Canvas* canvas, AppiButtonState* state); @@ -15,16 +15,48 @@ public: AppiButtonModeCyfralRead(AppiButton* 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) { if(event->type == AppiButtonEvent::EventTypeTick) { - uint8_t data[8]; - if(reader->read(data, 4)) { - memcpy(app->state.cyfral_address[app->state.cyfral_address_index], data, 4); - app->blink_green(); + // if we read a key + if(reader->read(keys[key_index], key_length)) { + // read next key + 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) { if(event->value.input.state && event->value.input.input == InputUp) { diff --git a/lib/cyfral/cyfral_reader_comp.h b/lib/cyfral/cyfral_reader_comp.h new file mode 100644 index 000000000..aa8f6768b --- /dev/null +++ b/lib/cyfral/cyfral_reader_comp.h @@ -0,0 +1,283 @@ +#pragma once +#include "flipper.h" +#include "flipper_v2.h" +#include "callback-connector.h" +#include + +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 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(comp_ctx); + COMP_HandleTypeDef* _hcomp = static_cast(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; +} \ No newline at end of file