浏览代码

[FL-2393][FL-2381] iButton, OneWire: move to plain C (#1068)

* iButton: getting started on the worker concept
* Hal delay: added global instructions_per_us variable
* iButton: one wire slave
* iButton: ibutton key setter
* iButton: one wire host, use ibutton_hal
* iButton\RFID: common pulse decoder concept
* iButton: cyfral decoder
* iButton: worker thread concept
* iButton: metakom decoder
* iButton: write key through worker
* iButton: worker mode holder
* iButton: worker improvements
* iButton: Cyfral encoder
* iButton: Metakom encoder
* lib: pulse protocol helpers
* iButton: Metakom decoder
* iButton: Cyfral decoder
* iButton worker: separate modes
* iButton: libs documentation
* HAL: iButton gpio modes
* iButton worker: rename modes file
* iButton worker, hal: move to LL
* iButton CLI: worker for reading and emulation commands
* iButton HAL: correct init and emulation sequence
* iButton cli: moved to plain C
* iButton: move to worker, small step to plain C
* Libs, one wire: move to plain C
* Libs: added forgotten files to compilation
* iButton writer: get rid of manual disable/enable irq
SG 3 年之前
父节点
当前提交
bdba15b366
共有 100 个文件被更改,包括 4442 次插入2351 次删除
  1. 7 6
      applications/accessor/accessor_app.cpp
  2. 3 7
      applications/accessor/accessor_app.h
  3. 4 4
      applications/accessor/scene/accessor_scene_start.cpp
  4. 0 193
      applications/ibutton/helpers/cyfral_decoder.cpp
  5. 0 55
      applications/ibutton/helpers/cyfral_decoder.h
  6. 0 39
      applications/ibutton/helpers/key_commands.h
  7. 0 208
      applications/ibutton/helpers/key_emulator.cpp
  8. 0 34
      applications/ibutton/helpers/key_emulator.h
  9. 0 11
      applications/ibutton/helpers/key_info.h
  10. 0 181
      applications/ibutton/helpers/key_reader.cpp
  11. 0 54
      applications/ibutton/helpers/key_reader.h
  12. 0 52
      applications/ibutton/helpers/key_worker.cpp
  13. 0 35
      applications/ibutton/helpers/key_worker.h
  14. 0 278
      applications/ibutton/helpers/key_writer.cpp
  15. 0 35
      applications/ibutton/helpers/key_writer.h
  16. 0 191
      applications/ibutton/helpers/metakom_decoder.cpp
  17. 0 54
      applications/ibutton/helpers/metakom_decoder.h
  18. 0 52
      applications/ibutton/helpers/pulse_sequencer.cpp
  19. 0 26
      applications/ibutton/helpers/pulse_sequencer.h
  20. 25 22
      applications/ibutton/ibutton_app.cpp
  21. 4 12
      applications/ibutton/ibutton_app.h
  22. 308 0
      applications/ibutton/ibutton_cli.c
  23. 0 292
      applications/ibutton/ibutton_cli.cpp
  24. 5 0
      applications/ibutton/ibutton_event.h
  25. 0 95
      applications/ibutton/ibutton_key.cpp
  26. 0 31
      applications/ibutton/ibutton_key.h
  27. 20 21
      applications/ibutton/scene/ibutton_scene_add_type.cpp
  28. 0 1
      applications/ibutton/scene/ibutton_scene_add_type.h
  29. 21 16
      applications/ibutton/scene/ibutton_scene_add_value.cpp
  30. 1 2
      applications/ibutton/scene/ibutton_scene_add_value.h
  31. 19 28
      applications/ibutton/scene/ibutton_scene_delete_confirm.cpp
  32. 0 3
      applications/ibutton/scene/ibutton_scene_delete_confirm.h
  33. 8 13
      applications/ibutton/scene/ibutton_scene_delete_success.cpp
  34. 0 3
      applications/ibutton/scene/ibutton_scene_delete_success.h
  35. 23 20
      applications/ibutton/scene/ibutton_scene_emulate.cpp
  36. 0 1
      applications/ibutton/scene/ibutton_scene_emulate.h
  37. 18 24
      applications/ibutton/scene/ibutton_scene_info.cpp
  38. 0 3
      applications/ibutton/scene/ibutton_scene_info.h
  39. 26 20
      applications/ibutton/scene/ibutton_scene_read.cpp
  40. 0 2
      applications/ibutton/scene/ibutton_scene_read.h
  41. 14 19
      applications/ibutton/scene/ibutton_scene_read_crc_error.cpp
  42. 0 4
      applications/ibutton/scene/ibutton_scene_read_crc_error.h
  43. 14 19
      applications/ibutton/scene/ibutton_scene_read_not_key_error.cpp
  44. 0 4
      applications/ibutton/scene/ibutton_scene_read_not_key_error.h
  45. 17 22
      applications/ibutton/scene/ibutton_scene_read_success.cpp
  46. 0 4
      applications/ibutton/scene/ibutton_scene_read_success.h
  47. 16 20
      applications/ibutton/scene/ibutton_scene_readed_key_menu.cpp
  48. 0 1
      applications/ibutton/scene/ibutton_scene_readed_key_menu.h
  49. 16 17
      applications/ibutton/scene/ibutton_scene_save_name.cpp
  50. 0 3
      applications/ibutton/scene/ibutton_scene_save_name.h
  51. 9 14
      applications/ibutton/scene/ibutton_scene_save_success.cpp
  52. 0 3
      applications/ibutton/scene/ibutton_scene_save_success.h
  53. 16 19
      applications/ibutton/scene/ibutton_scene_saved_key_menu.cpp
  54. 0 1
      applications/ibutton/scene/ibutton_scene_saved_key_menu.h
  55. 0 2
      applications/ibutton/scene/ibutton_scene_select_key.cpp
  56. 13 17
      applications/ibutton/scene/ibutton_scene_start.cpp
  57. 0 1
      applications/ibutton/scene/ibutton_scene_start.h
  58. 34 22
      applications/ibutton/scene/ibutton_scene_write.cpp
  59. 1 0
      applications/ibutton/scene/ibutton_scene_write.h
  60. 9 14
      applications/ibutton/scene/ibutton_scene_write_success.cpp
  61. 0 3
      applications/ibutton/scene/ibutton_scene_write_success.h
  62. 3 4
      firmware/targets/f7/furi_hal/furi_hal_delay.c
  63. 25 1
      firmware/targets/f7/furi_hal/furi_hal_ibutton.c
  64. 9 2
      firmware/targets/f7/furi_hal/furi_hal_rfid.c
  65. 2 0
      firmware/targets/furi_hal_include/furi_hal_delay.h
  66. 48 1
      firmware/targets/furi_hal_include/furi_hal_ibutton.h
  67. 5 10
      lib/lib.mk
  68. 126 0
      lib/one_wire/ibutton/encoder/encoder_cyfral.c
  69. 54 0
      lib/one_wire/ibutton/encoder/encoder_cyfral.h
  70. 93 0
      lib/one_wire/ibutton/encoder/encoder_metakom.c
  71. 54 0
      lib/one_wire/ibutton/encoder/encoder_metakom.h
  72. 121 0
      lib/one_wire/ibutton/ibutton_key.c
  73. 145 0
      lib/one_wire/ibutton/ibutton_key.h
  74. 28 0
      lib/one_wire/ibutton/ibutton_key_command.h
  75. 197 0
      lib/one_wire/ibutton/ibutton_worker.c
  76. 113 0
      lib/one_wire/ibutton/ibutton_worker.h
  77. 79 0
      lib/one_wire/ibutton/ibutton_worker_i.h
  78. 330 0
      lib/one_wire/ibutton/ibutton_worker_modes.c
  79. 298 0
      lib/one_wire/ibutton/ibutton_writer.c
  80. 60 0
      lib/one_wire/ibutton/ibutton_writer.h
  81. 256 0
      lib/one_wire/ibutton/pulse_protocols/protocol_cyfral.c
  82. 38 0
      lib/one_wire/ibutton/pulse_protocols/protocol_cyfral.h
  83. 262 0
      lib/one_wire/ibutton/pulse_protocols/protocol_metakom.c
  84. 38 0
      lib/one_wire/ibutton/pulse_protocols/protocol_metakom.h
  85. 16 0
      lib/one_wire/maxim_crc.c
  86. 14 0
      lib/one_wire/maxim_crc.h
  87. 59 0
      lib/one_wire/one_wire_device.c
  88. 74 0
      lib/one_wire/one_wire_device.h
  89. 261 0
      lib/one_wire/one_wire_host.c
  90. 121 0
      lib/one_wire/one_wire_host.h
  91. 30 0
      lib/one_wire/one_wire_host_timing.h
  92. 317 0
      lib/one_wire/one_wire_slave.c
  93. 71 0
      lib/one_wire/one_wire_slave.h
  94. 38 0
      lib/one_wire/one_wire_slave_i.h
  95. 66 0
      lib/one_wire/pulse_protocols/pulse_decoder.c
  96. 70 0
      lib/one_wire/pulse_protocols/pulse_decoder.h
  97. 55 0
      lib/one_wire/pulse_protocols/pulse_glue.c
  98. 26 0
      lib/one_wire/pulse_protocols/pulse_glue.h
  99. 67 0
      lib/one_wire/pulse_protocols/pulse_protocol.c
  100. 122 0
      lib/one_wire/pulse_protocols/pulse_protocol.h

+ 7 - 6
applications/accessor/accessor_app.cpp

@@ -9,7 +9,7 @@ void AccessorApp::run(void) {
     bool exit = false;
 
     wiegand.begin();
-    onewire_master.start();
+    onewire_host_start(onewire_host);
 
     scenes[current_scene]->on_enter(this);
 
@@ -28,19 +28,20 @@ void AccessorApp::run(void) {
     scenes[current_scene]->on_exit(this);
 
     wiegand.end();
-    onewire_master.stop();
+    onewire_host_stop(onewire_host);
 }
 
-AccessorApp::AccessorApp()
-    : onewire_master{&ibutton_gpio} {
+AccessorApp::AccessorApp() {
     furi_hal_power_insomnia_enter();
     notification = static_cast<NotificationApp*>(furi_record_open("notification"));
+    onewire_host = onewire_host_alloc();
     furi_hal_power_enable_otg();
 }
 
 AccessorApp::~AccessorApp() {
     furi_hal_power_disable_otg();
     furi_record_close("notification");
+    onewire_host_free(onewire_host);
     furi_hal_power_insomnia_exit();
 }
 
@@ -136,6 +137,6 @@ WIEGAND* AccessorApp::get_wiegand() {
     return &wiegand;
 }
 
-OneWireMaster* AccessorApp::get_one_wire() {
-    return &onewire_master;
+OneWireHost* AccessorApp::get_one_wire() {
+    return onewire_host;
 }

+ 3 - 7
applications/accessor/accessor_app.h

@@ -2,13 +2,9 @@
 #include <map>
 #include <list>
 #include "accessor_view_manager.h"
-
 #include "scene/accessor_scene_start.h"
-
 #include "helpers/wiegand.h"
-
-#include <one_wire_master.h>
-
+#include <one_wire/one_wire_host.h>
 #include <notification/notification_messages.h>
 
 class AccessorApp {
@@ -37,7 +33,7 @@ public:
     void set_text_store(const char* text...);
 
     WIEGAND* get_wiegand();
-    OneWireMaster* get_one_wire();
+    OneWireHost* get_one_wire();
 
 private:
     std::list<Scene> previous_scenes_list = {Scene::Exit};
@@ -52,7 +48,7 @@ private:
     char text_store[text_store_size + 1];
 
     WIEGAND wiegand;
-    OneWireMaster onewire_master;
+    OneWireHost* onewire_host;
 
     NotificationApp* notification;
 };

+ 4 - 4
applications/accessor/scene/accessor_scene_start.cpp

@@ -21,7 +21,7 @@ bool AccessorSceneStart::on_event(AccessorApp* app, AccessorEvent* event) {
     if(event->type == AccessorEvent::Type::Tick) {
         WIEGAND* wiegand = app->get_wiegand();
         Popup* popup = app->get_view_manager()->get_popup();
-        OneWireMaster* onewire = app->get_one_wire();
+        OneWireHost* onewire_host = app->get_one_wire();
 
         uint8_t data[8] = {0, 0, 0, 0, 0, 0, 0, 0};
         uint8_t type = 0;
@@ -38,11 +38,11 @@ bool AccessorSceneStart::on_event(AccessorApp* app, AccessorEvent* event) {
             }
         } else {
             FURI_CRITICAL_ENTER();
-            if(onewire->reset()) {
+            if(onewire_host_reset(onewire_host)) {
                 type = 255;
-                onewire->write(0x33);
+                onewire_host_write(onewire_host, 0x33);
                 for(uint8_t i = 0; i < 8; i++) {
-                    data[i] = onewire->read();
+                    data[i] = onewire_host_read(onewire_host);
                 }
 
                 for(uint8_t i = 0; i < 7; i++) {

+ 0 - 193
applications/ibutton/helpers/cyfral_decoder.cpp

@@ -1,193 +0,0 @@
-#include "cyfral_decoder.h"
-#include <furi.h>
-
-void CyfralDecoder::reset_state() {
-    state = State::WAIT_START_NIBBLE;
-    bit_state = BitState::WAIT_FRONT_LOW;
-
-    period_time = 0;
-    bit_index = 0;
-    ready = false;
-    index = 0;
-
-    key_data = 0;
-    readed_nibble = 0;
-    data_valid = true;
-}
-
-bool CyfralDecoder::nibble_valid(uint8_t data) {
-    uint8_t data_value = data & 0x0F;
-
-    switch(data_value) {
-    case 0b1110:
-    case 0b1101:
-    case 0b1011:
-    case 0b0111:
-        return true;
-        break;
-    default:
-        return false;
-    }
-}
-
-CyfralDecoder::CyfralDecoder() {
-    reset_state();
-    max_period = 0;
-}
-
-void CyfralDecoder::process_front(bool polarity, uint32_t time) {
-    bool readed;
-    bool value;
-
-    if(max_period == 0) {
-        max_period = 230 * (SystemCoreClock / 1000000.0f);
-    }
-
-    if(ready) return;
-
-    switch(state) {
-    case State::WAIT_START_NIBBLE:
-        // wait for start word
-        if(process_bit(polarity, time, &readed, &value)) {
-            if(readed) {
-                readed_nibble = ((readed_nibble << 1) | value) & 0x0F;
-                if(readed_nibble == 0b0001) {
-                    readed_nibble = 0;
-                    state = State::READ_NIBBLE;
-                }
-            }
-        } else {
-            reset_state();
-        }
-
-        break;
-    case State::READ_NIBBLE:
-        // read nibbles
-        if(process_bit(polarity, time, &readed, &value)) {
-            if(readed) {
-                readed_nibble = (readed_nibble << 1) | value;
-
-                bit_index++;
-
-                //convert every nibble to 2-bit index
-                if(bit_index == 4) {
-                    switch(readed_nibble) {
-                    case 0b1110:
-                        key_data = (key_data << 2) | 0b11;
-                        break;
-                    case 0b1101:
-                        key_data = (key_data << 2) | 0b10;
-                        break;
-                    case 0b1011:
-                        key_data = (key_data << 2) | 0b01;
-                        break;
-                    case 0b0111:
-                        key_data = (key_data << 2) | 0b00;
-                        break;
-                    default:
-                        data_valid = false;
-                        break;
-                    }
-
-                    readed_nibble = 0;
-                    bit_index = 0;
-                    index++;
-                }
-
-                // succefully read 8 nibbles
-                if(index == 8) {
-                    state = State::READ_STOP_NIBBLE;
-                }
-            }
-        } else {
-            reset_state();
-        }
-        break;
-    case State::READ_STOP_NIBBLE:
-        // read stop nibble
-        if(process_bit(polarity, time, &readed, &value)) {
-            if(readed) {
-                readed_nibble = ((readed_nibble << 1) | value) & 0x0F;
-                bit_index++;
-
-                switch(bit_index) {
-                case 0:
-                case 1:
-                case 2:
-                case 3:
-                    break;
-                case 4:
-                    if(readed_nibble == 0b0001) {
-                        // validate data
-                        if(data_valid) {
-                            ready = true;
-                        } else {
-                            reset_state();
-                        }
-                    } else {
-                        reset_state();
-                    }
-                    break;
-                default:
-                    reset_state();
-                    break;
-                }
-            }
-        } else {
-            reset_state();
-        }
-        break;
-    }
-}
-
-bool CyfralDecoder::process_bit(bool polarity, uint32_t time, bool* readed, bool* readed_value) {
-    bool result = true;
-    *readed = false;
-
-    // bit start from low
-    switch(bit_state) {
-    case BitState::WAIT_FRONT_LOW:
-        if(polarity == true) {
-            period_time += time;
-
-            *readed = true;
-            if(period_time <= max_period) {
-                if((period_time / 2) > time) {
-                    *readed_value = false;
-                } else {
-                    *readed_value = true;
-                }
-            } else {
-                result = false;
-            }
-
-            bit_state = BitState::WAIT_FRONT_HIGH;
-        } else {
-            result = false;
-        }
-        break;
-    case BitState::WAIT_FRONT_HIGH:
-        if(polarity == false) {
-            period_time = time;
-            bit_state = BitState::WAIT_FRONT_LOW;
-        } else {
-            result = false;
-        }
-        break;
-    }
-
-    return result;
-}
-
-bool CyfralDecoder::read(uint8_t* _data, uint8_t data_size) {
-    furi_check(data_size <= 2);
-    bool result = false;
-
-    if(ready) {
-        memcpy(_data, &key_data, data_size);
-        reset_state();
-        result = true;
-    }
-
-    return result;
-}

+ 0 - 55
applications/ibutton/helpers/cyfral_decoder.h

@@ -1,55 +0,0 @@
-#pragma once
-#include <stdint.h>
-#include <atomic>
-
-class CyfralDecoder {
-public:
-    bool read(uint8_t* data, uint8_t data_size);
-    void process_front(bool polarity, uint32_t time);
-
-    CyfralDecoder();
-
-private:
-    enum class BitState : uint8_t {
-        WAIT_FRONT_HIGH,
-        WAIT_FRONT_LOW,
-    };
-
-    enum class State : uint8_t {
-        WAIT_START_NIBBLE,
-        READ_NIBBLE,
-        READ_STOP_NIBBLE,
-    };
-
-    State state;
-    BitState bit_state;
-
-    bool process_bit(bool polarity, uint32_t time, bool* readed, bool* readed_value);
-    void reset_state();
-    bool nibble_valid(uint8_t data);
-
-    // high + low period time
-    uint32_t period_time;
-
-    // ready flag, key is readed and valid
-    std::atomic<bool> ready;
-
-    // key data storage
-    uint16_t key_data;
-
-    // temporary nibble storage
-    uint8_t readed_nibble;
-
-    // data valid flag
-    // MUST be checked only in READ_STOP_NIBBLE state
-    bool data_valid;
-
-    // nibble index, we expect 8 nibbles
-    uint8_t index;
-
-    // bit index in nibble, 4 bit per nibble
-    uint8_t bit_index;
-
-    // max period, 230us x clock per us
-    uint32_t max_period;
-};

+ 0 - 39
applications/ibutton/helpers/key_commands.h

@@ -1,39 +0,0 @@
-#pragma once
-#include <stdint.h>
-
-class RW1990_1 {
-public:
-    constexpr static const uint8_t CMD_WRITE_RECORD_FLAG = 0xD1;
-    constexpr static const uint8_t CMD_READ_RECORD_FLAG = 0xB5;
-    constexpr static const uint8_t CMD_WRITE_ROM = 0xD5;
-};
-
-class RW1990_2 {
-public:
-    constexpr static const uint8_t CMD_WRITE_RECORD_FLAG = 0x1D;
-    constexpr static const uint8_t CMD_READ_RECORD_FLAG = 0x1E;
-    constexpr static const uint8_t CMD_WRITE_ROM = 0xD5;
-};
-
-class TM2004 {
-public:
-    constexpr static const uint8_t CMD_READ_STATUS = 0xAA;
-    constexpr static const uint8_t CMD_READ_MEMORY = 0xF0;
-    constexpr static const uint8_t CMD_WRITE_ROM = 0x3C;
-    constexpr static const uint8_t CMD_FINALIZATION = 0x35;
-
-    constexpr static const uint8_t ANSWER_READ_MEMORY = 0xF5;
-};
-
-class TM01 {
-public:
-    constexpr static const uint8_t CMD_WRITE_RECORD_FLAG = 0xC1;
-    constexpr static const uint8_t CMD_WRITE_ROM = 0xC5;
-    constexpr static const uint8_t CMD_SWITCH_TO_CYFRAL = 0xCA;
-    constexpr static const uint8_t CMD_SWITCH_TO_METAKOM = 0xCB;
-};
-
-class DS1990 {
-public:
-    constexpr static const uint8_t CMD_READ_ROM = 0x33;
-};

+ 0 - 208
applications/ibutton/helpers/key_emulator.cpp

@@ -1,208 +0,0 @@
-#include "key_emulator.h"
-#include <callback-connector.h>
-
-KeyEmulator::~KeyEmulator() {
-    stop();
-}
-
-KeyEmulator::KeyEmulator(OneWireSlave* _onewire_slave)
-    : dallas_key{0, 0, 0, 0, 0, 0, 0} {
-    onewire_slave = _onewire_slave;
-
-    auto cb = cbc::obtain_connector(this, &KeyEmulator::result_callback);
-    onewire_slave->set_result_callback(cb, this);
-}
-
-void KeyEmulator::start(iButtonKey* key) {
-    anything_emulated = false;
-    stop();
-
-    // pulldown pull pin, to prevent low-pass filtering by the RFID part of the schematic
-    furi_hal_rfid_pin_pull_pulldown();
-
-    switch(key->get_key_type()) {
-    case iButtonKeyType::KeyDallas:
-        start_dallas_emulate(key);
-        break;
-    case iButtonKeyType::KeyCyfral:
-        start_cyfral_emulate(key);
-        break;
-    case iButtonKeyType::KeyMetakom:
-        start_metakom_emulate(key);
-        break;
-    }
-}
-
-bool KeyEmulator::emulated() {
-    bool result = false;
-
-    if(anything_emulated) {
-        anything_emulated = false;
-        result = true;
-    }
-
-    return result;
-}
-
-void KeyEmulator::stop() {
-    onewire_slave->stop();
-    pulser.stop();
-    furi_hal_rfid_pins_reset();
-}
-
-void KeyEmulator::start_cyfral_emulate(iButtonKey* key) {
-    furi_assert(key->get_key_type() == iButtonKeyType::KeyCyfral);
-    furi_assert(key->get_type_data_size() == 2);
-
-    const uint32_t cyfral_period_full = 8000;
-    const uint32_t cyfral_period_one[2] = {
-        uint32_t(cyfral_period_full * 0.33f), uint32_t(cyfral_period_full * 0.66f)};
-    const uint32_t cyfral_period_zero[2] = {
-        uint32_t(cyfral_period_full * 0.66f), uint32_t(cyfral_period_full * 0.33f)};
-    uint8_t pd_index = 0;
-    uint8_t* key_data = key->get_data();
-
-    // start nibble
-    set_pulse_data_cyfral(pd_index, cyfral_period_zero);
-    pd_index++;
-    set_pulse_data_cyfral(pd_index, cyfral_period_zero);
-    pd_index++;
-    set_pulse_data_cyfral(pd_index, cyfral_period_zero);
-    pd_index++;
-    set_pulse_data_cyfral(pd_index, cyfral_period_one);
-    pd_index++;
-
-    // data nibbles x 8
-    for(int8_t i = key->get_type_data_size() - 1; i >= 0; i--) {
-        for(int8_t j = 3; j >= 0; j--) {
-            switch((key_data[i] >> (j * 2)) & 0b00000011) {
-            case 0b11:
-                set_pulse_data_cyfral(pd_index, cyfral_period_one);
-                pd_index++;
-                set_pulse_data_cyfral(pd_index, cyfral_period_one);
-                pd_index++;
-                set_pulse_data_cyfral(pd_index, cyfral_period_one);
-                pd_index++;
-                set_pulse_data_cyfral(pd_index, cyfral_period_zero);
-                pd_index++;
-                break;
-            case 0b10:
-                set_pulse_data_cyfral(pd_index, cyfral_period_one);
-                pd_index++;
-                set_pulse_data_cyfral(pd_index, cyfral_period_one);
-                pd_index++;
-                set_pulse_data_cyfral(pd_index, cyfral_period_zero);
-                pd_index++;
-                set_pulse_data_cyfral(pd_index, cyfral_period_one);
-                pd_index++;
-                break;
-            case 0b01:
-                set_pulse_data_cyfral(pd_index, cyfral_period_one);
-                pd_index++;
-                set_pulse_data_cyfral(pd_index, cyfral_period_zero);
-                pd_index++;
-                set_pulse_data_cyfral(pd_index, cyfral_period_one);
-                pd_index++;
-                set_pulse_data_cyfral(pd_index, cyfral_period_one);
-                pd_index++;
-                break;
-            case 0b00:
-                set_pulse_data_cyfral(pd_index, cyfral_period_zero);
-                pd_index++;
-                set_pulse_data_cyfral(pd_index, cyfral_period_one);
-                pd_index++;
-                set_pulse_data_cyfral(pd_index, cyfral_period_one);
-                pd_index++;
-                set_pulse_data_cyfral(pd_index, cyfral_period_one);
-                pd_index++;
-                break;
-            default:
-                // cannot be anyway
-                furi_check(false);
-                break;
-            }
-        }
-    }
-
-    // 4 (nibbles) x (8 data + 1 start) = 4 x 9 = 36
-    if(pd_index != 36) {
-        // something is very wrong
-        furi_check(false);
-    }
-
-    pulser.set_periods(pulse_data, 72, false);
-    pulser.start();
-}
-
-void KeyEmulator::start_metakom_emulate(iButtonKey* key) {
-    furi_assert(key->get_key_type() == iButtonKeyType::KeyMetakom);
-    furi_assert(key->get_type_data_size() == 4);
-
-    const uint32_t metakom_period_full = 8000;
-    const uint32_t metakom_period_zero[2] = {
-        uint32_t(metakom_period_full * 0.33f), uint32_t(metakom_period_full * 0.66f)};
-    const uint32_t metakom_period_one[2] = {
-        uint32_t(metakom_period_full * 0.66f), uint32_t(metakom_period_full * 0.33f)};
-    uint8_t pd_index = 0;
-
-    uint8_t* key_data = key->get_data();
-
-    // start pulse
-    pulse_data[0] = metakom_period_full;
-
-    // start triplet
-    set_pulse_data_metakom(pd_index, metakom_period_zero);
-    pd_index++;
-    set_pulse_data_metakom(pd_index, metakom_period_one);
-    pd_index++;
-    set_pulse_data_metakom(pd_index, metakom_period_zero);
-    pd_index++;
-
-    for(int8_t i = key->get_type_data_size() - 1; i >= 0; i--) {
-        for(int8_t j = 7; j >= 0; j--) {
-            if(((key_data[i] >> j) & 0b00000001) == 1) {
-                set_pulse_data_metakom(pd_index, metakom_period_one);
-                pd_index++;
-            } else {
-                set_pulse_data_metakom(pd_index, metakom_period_zero);
-                pd_index++;
-            }
-        }
-    }
-
-    // 4 byte x 8 bits + 3 start bits = 35
-    if(pd_index != 35) {
-        // something is very wrong
-        furi_check(false);
-    }
-
-    pulser.set_periods(pulse_data, 71, false);
-    pulser.start();
-}
-
-void KeyEmulator::start_dallas_emulate(iButtonKey* key) {
-    furi_assert(key->get_key_type() == iButtonKeyType::KeyDallas);
-    furi_assert(key->get_type_data_size() == 8);
-
-    onewire_slave->deattach();
-    memcpy(dallas_key.id_storage, key->get_data(), key->get_type_data_size());
-    onewire_slave->attach(&dallas_key);
-    onewire_slave->start();
-}
-
-void KeyEmulator::set_pulse_data_cyfral(uint8_t index, const uint32_t* data) {
-    pulse_data[index * 2] = data[0];
-    pulse_data[index * 2 + 1] = data[1];
-}
-
-void KeyEmulator::set_pulse_data_metakom(uint8_t index, const uint32_t* data) {
-    // damn start pulse
-    pulse_data[(index * 2) + 1] = data[0];
-    pulse_data[(index * 2) + 2] = data[1];
-}
-
-void KeyEmulator::result_callback(bool success, void* ctx) {
-    KeyEmulator* _this = static_cast<KeyEmulator*>(ctx);
-
-    _this->anything_emulated = true;
-}

+ 0 - 34
applications/ibutton/helpers/key_emulator.h

@@ -1,34 +0,0 @@
-#pragma once
-#include "pulse_sequencer.h"
-#include "../ibutton_key.h"
-#include <one_wire_slave.h>
-#include <one_wire_device_ds_1990.h>
-#include <atomic>
-
-class KeyEmulator {
-public:
-    KeyEmulator(OneWireSlave* onewire_slave);
-    ~KeyEmulator();
-
-    void start(iButtonKey* key);
-    bool emulated();
-    void stop();
-
-private:
-    DS1990 dallas_key;
-    OneWireSlave* onewire_slave;
-
-    PulseSequencer pulser;
-    uint32_t pulse_data[72];
-
-    std::atomic<bool> anything_emulated;
-
-    void start_cyfral_emulate(iButtonKey* key);
-    void start_metakom_emulate(iButtonKey* key);
-    void start_dallas_emulate(iButtonKey* key);
-
-    void set_pulse_data_cyfral(uint8_t index, const uint32_t* data);
-    void set_pulse_data_metakom(uint8_t index, const uint32_t* data);
-
-    void result_callback(bool success, void* ctx);
-};

+ 0 - 11
applications/ibutton/helpers/key_info.h

@@ -1,11 +0,0 @@
-#pragma once
-#include <stdint.h>
-
-static const uint8_t IBUTTON_KEY_DATA_SIZE = 8;
-static const uint8_t IBUTTON_KEY_NAME_SIZE = 22;
-
-enum class iButtonKeyType : uint8_t {
-    KeyDallas,
-    KeyCyfral,
-    KeyMetakom,
-};

+ 0 - 181
applications/ibutton/helpers/key_reader.cpp

@@ -1,181 +0,0 @@
-#include "key_reader.h"
-#include "key_commands.h"
-#include <callback-connector.h>
-#include <maxim_crc.h>
-
-KeyReader::Error KeyReader::read(iButtonKey* key) {
-    uint8_t tmp_key_data[8] = {0, 0, 0, 0, 0, 0, 0, 0};
-    iButtonKeyType key_type;
-
-    KeyReader::Error result = KeyReader::Error::EMPTY;
-
-    if(read_key(&key_type, tmp_key_data, 8)) {
-        switch(key_type) {
-        case iButtonKeyType::KeyDallas:
-            if(verify_key(key_type, tmp_key_data, 8)) {
-                if(maxim_crc8(tmp_key_data, 8) == 0) {
-                    if(tmp_key_data[0] == 0x01) {
-                        result = KeyReader::Error::OK;
-                    } else {
-                        result = KeyReader::Error::NOT_ARE_KEY;
-                    }
-                } else {
-                    result = KeyReader::Error::CRC_ERROR;
-                }
-            }
-
-            break;
-        case iButtonKeyType::KeyCyfral:
-            result = KeyReader::Error::OK;
-            break;
-        case iButtonKeyType::KeyMetakom:
-            result = KeyReader::Error::OK;
-            break;
-        }
-
-        if(result != KeyReader::Error::EMPTY) {
-            key->set_type(key_type);
-            key->set_data(tmp_key_data, 8);
-        }
-    }
-
-    switch_mode_if_needed();
-
-    return result;
-}
-
-KeyReader::KeyReader(OneWireMaster* _onewire_master) {
-    onewire_master = _onewire_master;
-    read_mode_switch_time = 0;
-    read_mode = ReadMode::DALLAS;
-}
-
-KeyReader::~KeyReader() {
-    stop();
-}
-
-bool KeyReader::read_key(iButtonKeyType* key_type, uint8_t* data, uint8_t data_size) {
-    bool readed = false;
-
-    if(read_mode == ReadMode::DALLAS) {
-        FURI_CRITICAL_ENTER();
-        if(onewire_master->search(data)) {
-            onewire_master->reset_search();
-            readed = true;
-            *key_type = iButtonKeyType::KeyDallas;
-        } else {
-            onewire_master->reset_search();
-        }
-        FURI_CRITICAL_EXIT();
-    } else if(read_mode == ReadMode::CYFRAL_METAKOM) {
-        if(cyfral_decoder.read(data, 2)) {
-            readed = true;
-            *key_type = iButtonKeyType::KeyCyfral;
-        } else if(metakom_decoder.read(data, 4)) {
-            readed = true;
-            *key_type = iButtonKeyType::KeyMetakom;
-        }
-    }
-
-    return readed;
-}
-
-bool KeyReader::verify_key(iButtonKeyType key_type, const uint8_t* const data, uint8_t data_size) {
-    bool result = true;
-
-    if(key_type == iButtonKeyType::KeyDallas) {
-        switch_to(ReadMode::DALLAS);
-
-        FURI_CRITICAL_ENTER();
-        if(onewire_master->reset()) {
-            onewire_master->write(DS1990::CMD_READ_ROM);
-            for(uint8_t i = 0; i < data_size; i++) {
-                if(onewire_master->read() != data[i]) {
-                    result = false;
-                }
-            }
-        } else {
-            result = false;
-        }
-        FURI_CRITICAL_EXIT();
-
-    } else {
-        result = false;
-    }
-
-    return result;
-}
-
-void KeyReader::start_comaparator(void) {
-    furi_hal_rfid_pins_reset();
-
-    // pulldown pull pin, we sense the signal through the analog part of the RFID schematic
-    furi_hal_rfid_pin_pull_pulldown();
-
-    comparator_callback_pointer =
-        cbc::obtain_connector(this, &KeyReader::comparator_trigger_callback);
-    furi_hal_rfid_comp_set_callback(comparator_callback_pointer, this);
-    last_dwt_value = DWT->CYCCNT;
-    furi_hal_rfid_comp_start();
-}
-
-void KeyReader::stop_comaparator(void) {
-    furi_hal_rfid_pins_reset();
-
-    // rfid_pins_reset will disable ibutton pin
-    furi_hal_ibutton_start();
-
-    furi_hal_rfid_comp_stop();
-    furi_hal_rfid_comp_set_callback(NULL, NULL);
-}
-
-void KeyReader::comparator_trigger_callback(bool level, void* comp_ctx) {
-    KeyReader* _this = static_cast<KeyReader*>(comp_ctx);
-
-    uint32_t current_dwt_value = DWT->CYCCNT;
-
-    _this->cyfral_decoder.process_front(level, current_dwt_value - last_dwt_value);
-    _this->metakom_decoder.process_front(level, current_dwt_value - last_dwt_value);
-
-    last_dwt_value = current_dwt_value;
-}
-
-void KeyReader::switch_to(ReadMode mode) {
-    switch(mode) {
-    case ReadMode::DALLAS:
-        onewire_master->start();
-        stop_comaparator();
-        break;
-    case ReadMode::CYFRAL_METAKOM:
-        onewire_master->stop();
-        start_comaparator();
-        break;
-    }
-
-    read_mode = mode;
-}
-
-void KeyReader::switch_mode_if_needed() {
-    if(osKernelGetTickCount() - read_mode_switch_time > (osKernelGetTickFreq() / 5)) {
-        read_mode_switch_time = osKernelGetTickCount();
-        switch(read_mode) {
-        case ReadMode::DALLAS:
-            switch_to(ReadMode::CYFRAL_METAKOM);
-            break;
-        case ReadMode::CYFRAL_METAKOM:
-            switch_to(ReadMode::DALLAS);
-            break;
-        }
-    }
-}
-
-void KeyReader::start() {
-    furi_hal_power_enable_otg();
-    switch_to(ReadMode::CYFRAL_METAKOM);
-}
-
-void KeyReader::stop() {
-    furi_hal_power_disable_otg();
-    onewire_master->stop();
-    stop_comaparator();
-}

+ 0 - 54
applications/ibutton/helpers/key_reader.h

@@ -1,54 +0,0 @@
-#pragma once
-#include <stdint.h>
-#include <furi.h>
-#include "cyfral_decoder.h"
-#pragma once
-#include "metakom_decoder.h"
-#include "../ibutton_key.h"
-#include <one_wire_master.h>
-#include <one_wire_slave.h>
-
-class KeyReader {
-public:
-    enum class Error : uint8_t {
-        EMPTY,
-        CRC_ERROR,
-        NOT_ARE_KEY,
-        OK,
-    };
-
-    void start();
-    void stop();
-    KeyReader::Error read(iButtonKey* key);
-    KeyReader(OneWireMaster* onewire_master);
-    ~KeyReader();
-
-private:
-    bool read_key(iButtonKeyType* key_type, uint8_t* data, uint8_t data_size);
-    bool verify_key(iButtonKeyType key_type, const uint8_t* const data, uint8_t data_size);
-
-    // cyfral and metakom readers data
-    void comparator_trigger_callback(bool level, void* comp_ctx);
-    void (*comparator_callback_pointer)(bool level, void* comp_ctx);
-
-    void start_comaparator(void);
-    void stop_comaparator(void);
-    uint32_t last_dwt_value;
-
-    CyfralDecoder cyfral_decoder;
-    MetakomDecoder metakom_decoder;
-
-    // mode
-    uint32_t read_mode_switch_time;
-    enum class ReadMode : uint8_t {
-        CYFRAL_METAKOM,
-        DALLAS,
-    };
-    ReadMode read_mode;
-
-    // one wire
-    OneWireMaster* onewire_master;
-
-    void switch_to(ReadMode mode);
-    void switch_mode_if_needed();
-};

+ 0 - 52
applications/ibutton/helpers/key_worker.cpp

@@ -1,52 +0,0 @@
-#include "key_worker.h"
-#include <callback-connector.h>
-#include <maxim_crc.h>
-
-KeyReader::Error KeyWorker::read(iButtonKey* key) {
-    KeyReader::Error result = key_reader.read(key);
-
-    return result;
-}
-
-void KeyWorker::start_read() {
-    key_reader.start();
-}
-
-void KeyWorker::stop_read() {
-    key_reader.stop();
-}
-
-bool KeyWorker::emulated() {
-    return key_emulator.emulated();
-}
-
-void KeyWorker::start_emulate(iButtonKey* key) {
-    key_emulator.start(key);
-}
-
-void KeyWorker::stop_emulate() {
-    key_emulator.stop();
-}
-
-KeyWriter::Error KeyWorker::write(iButtonKey* key) {
-    return key_writer.write(key);
-}
-
-void KeyWorker::start_write() {
-    key_writer.start();
-}
-
-void KeyWorker::stop_write() {
-    key_writer.stop();
-}
-
-KeyWorker::KeyWorker(const GpioPin* one_wire_gpio)
-    : onewire_master{one_wire_gpio}
-    , onewire_slave{one_wire_gpio}
-    , key_reader{&onewire_master}
-    , key_emulator{&onewire_slave}
-    , key_writer{&onewire_master} {
-}
-
-KeyWorker::~KeyWorker() {
-}

+ 0 - 35
applications/ibutton/helpers/key_worker.h

@@ -1,35 +0,0 @@
-#pragma once
-#include <furi.h>
-#include "key_info.h"
-#include "key_reader.h"
-#include "key_emulator.h"
-#include "key_writer.h"
-#include "../ibutton_key.h"
-#include <one_wire_master.h>
-#include <one_wire_slave.h>
-
-class KeyWorker {
-public:
-    KeyReader::Error read(iButtonKey* key);
-    void start_read();
-    void stop_read();
-
-    bool emulated();
-    void start_emulate(iButtonKey* key);
-    void stop_emulate();
-
-    KeyWriter::Error write(iButtonKey* key);
-    void start_write();
-    void stop_write();
-
-    KeyWorker(const GpioPin* one_wire_gpio);
-    ~KeyWorker();
-
-private:
-    // one wire
-    OneWireMaster onewire_master;
-    OneWireSlave onewire_slave;
-    KeyReader key_reader;
-    KeyEmulator key_emulator;
-    KeyWriter key_writer;
-};

+ 0 - 278
applications/ibutton/helpers/key_writer.cpp

@@ -1,278 +0,0 @@
-#include "key_writer.h"
-#include "key_commands.h"
-
-KeyWriter::KeyWriter(OneWireMaster* _onewire_master) {
-    onewire_master = _onewire_master;
-}
-
-KeyWriter::~KeyWriter() {
-    stop();
-}
-
-KeyWriter::Error KeyWriter::write(iButtonKey* key) {
-    return write_internal(key);
-}
-
-void KeyWriter::start() {
-    furi_hal_power_enable_otg();
-    onewire_master->start();
-}
-
-void KeyWriter::stop() {
-    furi_hal_power_disable_otg();
-    onewire_master->stop();
-}
-
-KeyWriter::Error KeyWriter::write_internal(iButtonKey* key) {
-    Error result = Error::NO_DETECT;
-    bool same_key = false;
-
-    osKernelLock();
-    bool presence = onewire_master->reset();
-    osKernelUnlock();
-
-    if(presence) {
-        switch(key->get_key_type()) {
-        case iButtonKeyType::KeyDallas:
-            same_key = compare_key_ds1990(key);
-
-            if(!same_key) {
-                bool write_result = false;
-                // currently we can write:
-                // RW1990, TM08v2, TM08vi-2 by write_1990_1()
-                // RW2004, RW2004 with EEPROM by write_TM2004();
-
-                if(!write_result) {
-                    write_result = write_1990_1(key);
-                }
-                if(!write_result) {
-                    write_result = write_1990_2(key);
-                }
-                if(!write_result) {
-                    write_result = write_TM2004(key);
-                }
-
-                if(write_result) {
-                    result = Error::OK;
-                } else {
-                    result = Error::CANNOT_WRITE;
-                }
-            } else {
-                result = Error::SAME_KEY;
-            }
-            break;
-
-        default:
-            break;
-        }
-    }
-
-    return result;
-}
-
-bool KeyWriter::compare_key_ds1990(iButtonKey* key) {
-    bool result = false;
-
-    if(key->get_key_type() == iButtonKeyType::KeyDallas) {
-        FURI_CRITICAL_ENTER();
-        bool presence = onewire_master->reset();
-
-        if(presence) {
-            onewire_master->write(DS1990::CMD_READ_ROM);
-
-            result = true;
-            for(uint8_t i = 0; i < key->get_type_data_size(); i++) {
-                if(key->get_data()[i] != onewire_master->read()) {
-                    result = false;
-                    break;
-                }
-            }
-        }
-
-        FURI_CRITICAL_EXIT();
-    }
-
-    return result;
-}
-
-bool KeyWriter::write_1990_1(iButtonKey* key) {
-    bool result = false;
-
-    if(key->get_key_type() == iButtonKeyType::KeyDallas) {
-        FURI_CRITICAL_ENTER();
-
-        // unlock
-        onewire_master->reset();
-        onewire_master->write(RW1990_1::CMD_WRITE_RECORD_FLAG);
-        delay_us(10);
-        onewire_write_one_bit(0, 5000);
-
-        // write key
-        onewire_master->reset();
-        onewire_master->write(RW1990_1::CMD_WRITE_ROM);
-        for(uint8_t i = 0; i < key->get_type_data_size(); i++) {
-            // inverted key for RW1990.1
-            write_byte_ds1990(~key->get_data()[i]);
-            delay_us(30000);
-        }
-
-        // lock
-        onewire_master->write(RW1990_1::CMD_WRITE_RECORD_FLAG);
-        onewire_write_one_bit(1);
-
-        FURI_CRITICAL_EXIT();
-
-        if(compare_key_ds1990(key)) {
-            result = true;
-        }
-    }
-
-    return result;
-}
-
-bool KeyWriter::write_1990_2(iButtonKey* key) {
-    bool result = false;
-
-    if(key->get_key_type() == iButtonKeyType::KeyDallas) {
-        FURI_CRITICAL_ENTER();
-
-        // unlock
-        onewire_master->reset();
-        onewire_master->write(RW1990_2::CMD_WRITE_RECORD_FLAG);
-        delay_us(10);
-        onewire_write_one_bit(1, 5000);
-
-        // write key
-        onewire_master->reset();
-        onewire_master->write(RW1990_2::CMD_WRITE_ROM);
-        for(uint8_t i = 0; i < key->get_type_data_size(); i++) {
-            write_byte_ds1990(key->get_data()[i]);
-            delay_us(30000);
-        }
-
-        // lock
-        onewire_master->write(RW1990_2::CMD_WRITE_RECORD_FLAG);
-        onewire_write_one_bit(0);
-
-        FURI_CRITICAL_EXIT();
-
-        if(compare_key_ds1990(key)) {
-            result = true;
-        }
-    }
-
-    return result;
-}
-
-bool KeyWriter::write_TM2004(iButtonKey* key) {
-    uint8_t answer;
-    bool result = true;
-
-    if(key->get_key_type() == iButtonKeyType::KeyDallas) {
-        FURI_CRITICAL_ENTER();
-
-        // write rom, addr is 0x0000
-        onewire_master->reset();
-        onewire_master->write(TM2004::CMD_WRITE_ROM);
-        onewire_master->write(0x00);
-        onewire_master->write(0x00);
-
-        // write key
-        for(uint8_t i = 0; i < key->get_type_data_size(); i++) {
-            // write key byte
-            onewire_master->write(key->get_data()[i]);
-            answer = onewire_master->read();
-            // TODO: check answer CRC
-
-            // pulse indicating that data is correct
-            delay_us(600);
-            onewire_write_one_bit(1, 50000);
-
-            // read writed key byte
-            answer = onewire_master->read();
-
-            // check that writed and readed are same
-            if(key->get_data()[i] != answer) {
-                result = false;
-                break;
-            }
-        }
-
-        if(!compare_key_ds1990(key)) {
-            result = false;
-        }
-
-        onewire_master->reset();
-
-        FURI_CRITICAL_EXIT();
-    } else {
-        result = false;
-    }
-
-    return result;
-}
-
-bool KeyWriter::write_TM01(iButtonKey* key) {
-    /*bool result = true;
-
-    // TODO test and encoding
-    FURI_CRITICAL_ENTER();
-
-    // unlock
-    onewire_master->reset();
-    onewire_master->write(TM01::CMD_WRITE_RECORD_FLAG);
-    onewire_write_one_bit(1, 10000);
-
-    // write key
-    onewire_master->reset();
-    onewire_master->write(TM01::CMD_WRITE_ROM);
-
-    // TODO: key types
-    //if(type == KEY_METAKOM || type == KEY_CYFRAL) {
-    //} else {
-    for(uint8_t i = 0; i < key->get_type_data_size(); i++) {
-        write_byte_ds1990(key->get_data()[i]);
-        delay_us(10000);
-    }
-    //}
-
-    // lock
-    onewire_master->write(TM01::CMD_WRITE_RECORD_FLAG);
-    onewire_write_one_bit(0, 10000);
-
-    FURI_CRITICAL_EXIT();
-
-    if(!compare_key_ds1990(key)) {
-        result = false;
-    }
-
-    FURI_CRITICAL_ENTER();
-
-    if(key->get_key_type() == iButtonKeyType::KeyMetakom ||
-       key->get_key_type() == iButtonKeyType::KeyCyfral) {
-        onewire_master->reset();
-        if(key->get_key_type() == iButtonKeyType::KeyCyfral)
-            onewire_master->write(TM01::CMD_SWITCH_TO_CYFRAL);
-        else
-            onewire_master->write(TM01::CMD_SWITCH_TO_METAKOM);
-        onewire_write_one_bit(1);
-    }
-
-    FURI_CRITICAL_EXIT();
-
-    return result;*/
-    return false;
-}
-
-void KeyWriter::onewire_write_one_bit(bool value, uint32_t delay) {
-    onewire_master->write_bit(value);
-    delay_us(delay);
-}
-
-void KeyWriter::write_byte_ds1990(uint8_t data) {
-    for(uint8_t n_bit = 0; n_bit < 8; n_bit++) {
-        onewire_master->write_bit(data & 1);
-        delay_us(5000);
-        data = data >> 1;
-    }
-}

+ 0 - 35
applications/ibutton/helpers/key_writer.h

@@ -1,35 +0,0 @@
-#pragma once
-#include "../ibutton_key.h"
-#include <one_wire_master.h>
-
-class KeyWriter {
-public:
-    enum class Error : uint8_t {
-        OK,
-        SAME_KEY,
-        NO_DETECT,
-        CANNOT_WRITE,
-    };
-
-    KeyWriter(OneWireMaster* onewire_master);
-    ~KeyWriter();
-
-    KeyWriter::Error write(iButtonKey* key);
-    void start();
-    void stop();
-
-private:
-    OneWireMaster* onewire_master;
-
-    KeyWriter::Error write_internal(iButtonKey* key);
-    bool compare_key_ds1990(iButtonKey* key);
-
-    // write strategy
-    bool write_1990_1(iButtonKey* key);
-    bool write_1990_2(iButtonKey* key);
-    bool write_TM2004(iButtonKey* key);
-    bool write_TM01(iButtonKey* key);
-
-    void onewire_write_one_bit(bool value, uint32_t delay = 10000);
-    void write_byte_ds1990(uint8_t data);
-};

+ 0 - 191
applications/ibutton/helpers/metakom_decoder.cpp

@@ -1,191 +0,0 @@
-#include "metakom_decoder.h"
-#include <furi.h>
-
-bool MetakomDecoder::read(uint8_t* _data, uint8_t data_size) {
-    bool result = false;
-
-    if(ready) {
-        memcpy(_data, &key_data, 4);
-        reset_state();
-        result = true;
-    }
-
-    return result;
-}
-
-void MetakomDecoder::process_front(bool polarity, uint32_t time) {
-    if(max_period == 0) {
-        max_period = 230 * (SystemCoreClock / 1000000.0f);
-    }
-
-    if(ready) return;
-
-    uint32_t high_time = 0;
-    uint32_t low_time = 0;
-
-    switch(state) {
-    case State::WAIT_PERIOD_SYNC:
-        if(process_bit(polarity, time, &high_time, &low_time)) {
-            period_sample_data[period_sample_index] = high_time + low_time;
-            period_sample_index++;
-
-            if(period_sample_index == period_sample_count) {
-                for(uint8_t i = 0; i < period_sample_count; i++) {
-                    period_time += period_sample_data[i];
-                };
-                period_time /= period_sample_count;
-
-                state = State::WAIT_START_BIT;
-            }
-        }
-
-        break;
-    case State::WAIT_START_BIT:
-        if(process_bit(polarity, time, &high_time, &low_time)) {
-            tmp_counter++;
-            if(high_time > period_time) {
-                tmp_counter = 0;
-                state = State::WAIT_START_WORD;
-            }
-
-            if(tmp_counter > 40) {
-                reset_state();
-            }
-        }
-
-        break;
-    case State::WAIT_START_WORD:
-        if(process_bit(polarity, time, &high_time, &low_time)) {
-            if(low_time < (period_time / 2)) {
-                tmp_data = (tmp_data << 1) | 0b0;
-            } else {
-                tmp_data = (tmp_data << 1) | 0b1;
-            }
-            tmp_counter++;
-
-            if(tmp_counter == 3) {
-                if(tmp_data == 0b010) {
-                    tmp_counter = 0;
-                    tmp_data = 0;
-                    state = State::READ_WORD;
-                } else {
-                    reset_state();
-                }
-            }
-        }
-        break;
-    case State::READ_WORD:
-        if(process_bit(polarity, time, &high_time, &low_time)) {
-            if(low_time < (period_time / 2)) {
-                tmp_data = (tmp_data << 1) | 0b0;
-            } else {
-                tmp_data = (tmp_data << 1) | 0b1;
-            }
-            tmp_counter++;
-
-            if(tmp_counter == 8) {
-                if(parity_check(tmp_data)) {
-                    key_data = (key_data << 8) | tmp_data;
-                    key_data_index++;
-                    tmp_data = 0;
-                    tmp_counter = 0;
-
-                    if(key_data_index == 4) {
-                        // check for stop bit
-                        if(high_time > period_time) {
-                            state = State::READ_STOP_WORD;
-                        } else {
-                            reset_state();
-                        }
-                    }
-                } else {
-                    reset_state();
-                }
-            }
-        }
-        break;
-    case State::READ_STOP_WORD:
-        if(process_bit(polarity, time, &high_time, &low_time)) {
-            if(low_time < (period_time / 2)) {
-                tmp_data = (tmp_data << 1) | 0b0;
-            } else {
-                tmp_data = (tmp_data << 1) | 0b1;
-            }
-            tmp_counter++;
-
-            if(tmp_counter == 3) {
-                if(tmp_data == 0b010) {
-                    ready = true;
-                } else {
-                    reset_state();
-                }
-            }
-        }
-        break;
-    }
-}
-
-MetakomDecoder::MetakomDecoder() {
-    reset_state();
-}
-
-void MetakomDecoder::reset_state() {
-    ready = false;
-    period_sample_index = 0;
-    period_time = 0;
-
-    tmp_counter = 0;
-    tmp_data = 0;
-
-    for(uint8_t i = 0; i < period_sample_count; i++) {
-        period_sample_data[i] = 0;
-    };
-
-    state = State::WAIT_PERIOD_SYNC;
-    bit_state = BitState::WAIT_FRONT_LOW;
-
-    key_data = 0;
-    key_data_index = 0;
-}
-
-bool MetakomDecoder::parity_check(uint8_t data) {
-    uint8_t ones_count = 0;
-    bool result;
-
-    for(uint8_t i = 0; i < 8; i++) {
-        if((data >> i) & 0b00000001) {
-            ones_count++;
-        }
-    }
-
-    result = (ones_count % 2 == 0);
-
-    return result;
-}
-
-bool MetakomDecoder::process_bit(
-    bool polarity,
-    uint32_t time,
-    uint32_t* high_time,
-    uint32_t* low_time) {
-    bool result = false;
-
-    switch(bit_state) {
-    case BitState::WAIT_FRONT_LOW:
-        if(polarity == false) {
-            *low_time = low_time_storage;
-            *high_time = time;
-            result = true;
-            bit_state = BitState::WAIT_FRONT_HIGH;
-        }
-        break;
-    case BitState::WAIT_FRONT_HIGH:
-        if(polarity == true) {
-            low_time_storage = time;
-            bit_state = BitState::WAIT_FRONT_LOW;
-        }
-        break;
-    }
-
-    return result;
-}

+ 0 - 54
applications/ibutton/helpers/metakom_decoder.h

@@ -1,54 +0,0 @@
-#pragma once
-#include <stdint.h>
-#include <atomic>
-
-class MetakomDecoder {
-public:
-    bool read(uint8_t* data, uint8_t data_size);
-    void process_front(bool polarity, uint32_t time);
-
-    MetakomDecoder();
-
-private:
-    enum class BitState : uint8_t {
-        WAIT_FRONT_HIGH,
-        WAIT_FRONT_LOW,
-    };
-
-    BitState bit_state;
-
-    enum class State : uint8_t {
-        WAIT_PERIOD_SYNC,
-        WAIT_START_BIT,
-        WAIT_START_WORD,
-        READ_WORD,
-        READ_STOP_WORD,
-    };
-
-    State state;
-
-    // high + low period time
-    uint32_t period_time;
-    uint32_t low_time_storage;
-
-    static const uint8_t period_sample_count = 10;
-    uint8_t period_sample_index;
-    uint32_t period_sample_data[period_sample_count];
-
-    // ready flag, key is readed and valid
-    std::atomic<bool> ready;
-
-    // max period, 230us x clock per us
-    uint32_t max_period;
-
-    uint8_t tmp_data;
-    uint8_t tmp_counter;
-
-    uint32_t key_data;
-    uint8_t key_data_index;
-
-    void reset_state();
-    bool parity_check(uint8_t data);
-
-    bool process_bit(bool polarity, uint32_t time, uint32_t* high_time, uint32_t* low_time);
-};

+ 0 - 52
applications/ibutton/helpers/pulse_sequencer.cpp

@@ -1,52 +0,0 @@
-#include "pulse_sequencer.h"
-
-#include <furi.h>
-#include <furi_hal.h>
-
-void PulseSequencer::set_periods(
-    uint32_t* _periods,
-    uint16_t _periods_count,
-    bool _pin_start_state) {
-    periods = _periods;
-    periods_count = _periods_count;
-    pin_start_state = _pin_start_state;
-}
-
-void PulseSequencer::start() {
-    period_index = 1;
-    pin_state = pin_start_state;
-    hal_gpio_write(&ibutton_gpio, pin_state);
-    pin_state = !pin_state;
-
-    hal_gpio_init(&ibutton_gpio, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedVeryHigh);
-    furi_hal_ibutton_emulate_start(
-        periods[period_index], PulseSequencer::timer_elapsed_callback, this);
-}
-
-void PulseSequencer::stop() {
-    furi_hal_ibutton_emulate_stop();
-}
-
-PulseSequencer::~PulseSequencer() {
-    stop();
-}
-
-void PulseSequencer::timer_elapsed_callback(void* context) {
-    PulseSequencer* self = static_cast<PulseSequencer*>(context);
-
-    furi_hal_ibutton_emulate_set_next(self->periods[self->period_index]);
-
-    if(self->period_index == 0) {
-        self->pin_state = self->pin_start_state;
-    } else {
-        self->pin_state = !self->pin_state;
-    }
-
-    hal_gpio_write(&ibutton_gpio, self->pin_state);
-
-    self->period_index++;
-
-    if(self->period_index == self->periods_count) {
-        self->period_index = 0;
-    }
-}

+ 0 - 26
applications/ibutton/helpers/pulse_sequencer.h

@@ -1,26 +0,0 @@
-#pragma once
-#include <stdint.h>
-
-class PulseSequencer {
-public:
-    void set_periods(uint32_t* periods, uint16_t periods_count, bool pin_start_state);
-    void start();
-    void stop();
-
-    ~PulseSequencer();
-
-private:
-    uint16_t period_index;
-    uint16_t periods_count;
-    uint32_t* periods;
-    bool pin_start_state;
-    bool pin_state;
-
-    void init_timer(uint32_t period);
-
-    void reset_period_index(PulseSequencer* _this);
-
-    void (*callback_pointer)(void*, void*);
-
-    static void timer_elapsed_callback(void* comp_ctx);
-};

+ 25 - 22
applications/ibutton/ibutton_app.cpp

@@ -1,6 +1,6 @@
 #include "ibutton_app.h"
 #include <stdarg.h>
-#include <callback-connector.h>
+#include <furi_hal.h>
 #include <m-string.h>
 #include <toolbox/path.h>
 #include <flipper_format/flipper_format.h>
@@ -42,7 +42,9 @@ iButtonApp::iButtonApp()
     , storage{"storage"}
     , dialogs{"dialogs"} {
     furi_hal_power_insomnia_enter();
-    key_worker = new KeyWorker(&ibutton_gpio);
+    key = ibutton_key_alloc();
+    key_worker = ibutton_worker_alloc();
+    ibutton_worker_start_thread(key_worker);
 }
 
 iButtonApp::~iButtonApp() {
@@ -50,7 +52,10 @@ iButtonApp::~iButtonApp() {
         delete it->second;
     }
     scenes.clear();
-    delete key_worker;
+
+    ibutton_worker_stop_thread(key_worker);
+    ibutton_worker_free(key_worker);
+    ibutton_key_free(key);
 
     furi_hal_power_insomnia_exit();
 }
@@ -112,17 +117,12 @@ iButtonApp::Scene iButtonApp::get_previous_scene() {
     return scene;
 }
 
-const GpioPin* iButtonApp::get_ibutton_pin() {
-    // TODO open record
-    return &ibutton_gpio;
-}
-
-KeyWorker* iButtonApp::get_key_worker() {
+iButtonWorker* iButtonApp::get_key_worker() {
     return key_worker;
 }
 
 iButtonKey* iButtonApp::get_key() {
-    return &key;
+    return key;
 }
 
 char* iButtonApp::get_file_name() {
@@ -201,10 +201,11 @@ bool iButtonApp::save_key(const char* key_name) {
         if(!delete_key()) break;
 
         // Save the key
-        key.set_name(key_name);
+        ibutton_key_set_name(key, key_name);
 
         // Set full file name, for new key
-        string_printf(key_file_name, "%s/%s%s", app_folder, key.get_name(), app_extension);
+        string_printf(
+            key_file_name, "%s/%s%s", app_folder, ibutton_key_get_name_p(key), app_extension);
 
         // Open file for write
         if(!flipper_format_file_open_always(file, string_get_cstr(key_file_name))) break;
@@ -215,7 +216,7 @@ bool iButtonApp::save_key(const char* key_name) {
         // Write key type
         if(!flipper_format_write_comment_cstr(file, "Key type can be Cyfral, Dallas or Metakom"))
             break;
-        const char* key_type = key.get_key_type_string_by_type(key.get_key_type());
+        const char* key_type = ibutton_key_get_string_by_type(ibutton_key_get_type(key));
         if(!flipper_format_write_string_cstr(file, "Key type", key_type)) break;
 
         // Write data
@@ -223,7 +224,8 @@ bool iButtonApp::save_key(const char* key_name) {
                file, "Data size for Cyfral is 2, for Metakom is 4, for Dallas is 8"))
             break;
 
-        if(!flipper_format_write_hex(file, "Data", key.get_data(), key.get_type_data_size()))
+        if(!flipper_format_write_hex(
+               file, "Data", ibutton_key_get_data_p(key), ibutton_key_get_data_size(key)))
             break;
         result = true;
 
@@ -258,15 +260,15 @@ bool iButtonApp::load_key_data(string_t key_path) {
         // key type
         iButtonKeyType type;
         if(!flipper_format_read_string(file, "Key type", data)) break;
-        if(!key.get_key_type_by_type_string(string_get_cstr(data), &type)) break;
+        if(!ibutton_key_get_type_by_string(string_get_cstr(data), &type)) break;
 
         // key data
         uint8_t key_data[IBUTTON_KEY_DATA_SIZE] = {0};
-        if(!flipper_format_read_hex(file, "Data", key_data, key.get_type_data_size_by_type(type)))
+        if(!flipper_format_read_hex(file, "Data", key_data, ibutton_key_get_size_by_type(type)))
             break;
 
-        key.set_type(type);
-        key.set_data(key_data, IBUTTON_KEY_DATA_SIZE);
+        ibutton_key_set_type(key, type);
+        ibutton_key_set_data(key, key_data, IBUTTON_KEY_DATA_SIZE);
 
         result = true;
     } while(false);
@@ -290,7 +292,7 @@ bool iButtonApp::load_key(const char* key_name) {
     result = load_key_data(key_path);
     if(result) {
         path_extract_filename_no_ext(key_name, key_path);
-        get_key()->set_name(string_get_cstr(key_path));
+        ibutton_key_set_name(key, string_get_cstr(key_path));
     }
     string_clear(key_path);
     return result;
@@ -306,7 +308,7 @@ bool iButtonApp::load_key() {
         app_extension,
         get_file_name(),
         get_file_name_size(),
-        get_key()->get_name());
+        ibutton_key_get_name_p(key));
 
     if(res) {
         string_t key_str;
@@ -316,7 +318,7 @@ bool iButtonApp::load_key() {
 
         result = load_key_data(key_str);
         if(result) {
-            get_key()->set_name(get_file_name());
+            ibutton_key_set_name(key, get_file_name());
         }
         string_clear(key_str);
     }
@@ -328,7 +330,8 @@ bool iButtonApp::delete_key() {
     string_t file_name;
     bool result = false;
 
-    string_init_printf(file_name, "%s/%s%s", app_folder, get_key()->get_name(), app_extension);
+    string_init_printf(
+        file_name, "%s/%s%s", app_folder, ibutton_key_get_name_p(key), app_extension);
     result = storage_simply_remove(storage, string_get_cstr(file_name));
     string_clear(file_name);
 

+ 4 - 12
applications/ibutton/ibutton_app.h

@@ -22,17 +22,10 @@
 #include "scene/ibutton_scene_select_key.h"
 #include "scene/ibutton_scene_add_type.h"
 #include "scene/ibutton_scene_add_value.h"
-
-#include "helpers/key_worker.h"
-
-#include "one_wire_master.h"
-#include "maxim_crc.h"
-#include "ibutton_key.h"
-
+#include <one_wire/ibutton/ibutton_worker.h>
 #include <notification/notification_messages.h>
 #include <storage/storage.h>
 #include <dialogs/dialogs.h>
-
 #include <record_controller.hpp>
 
 class iButtonApp {
@@ -75,7 +68,7 @@ public:
     Scene get_previous_scene();
 
     const GpioPin* get_ibutton_pin();
-    KeyWorker* get_key_worker();
+    iButtonWorker* get_key_worker();
     iButtonKey* get_key();
 
     void notify_green_blink();
@@ -127,9 +120,8 @@ private:
         {Scene::SceneAddValue, new iButtonSceneAddValue()},
     };
 
-    KeyWorker* key_worker;
-
-    iButtonKey key;
+    iButtonWorker* key_worker;
+    iButtonKey* key;
 
     RecordController<NotificationApp> notification;
     RecordController<Storage> storage;

+ 308 - 0
applications/ibutton/ibutton_cli.c

@@ -0,0 +1,308 @@
+#include <furi.h>
+#include <furi_hal.h>
+#include <stdarg.h>
+#include <cli/cli.h>
+#include <lib/toolbox/args.h>
+#include <one_wire/ibutton/ibutton_worker.h>
+#include <one_wire/one_wire_host.h>
+
+void ibutton_cli(Cli* cli, string_t args, void* context);
+void onewire_cli(Cli* cli, string_t args, void* context);
+
+// app cli function
+void ibutton_on_system_start() {
+#ifdef SRV_CLI
+    Cli* cli = furi_record_open("cli");
+    cli_add_command(cli, "ikey", CliCommandFlagDefault, ibutton_cli, cli);
+    cli_add_command(cli, "onewire", CliCommandFlagDefault, onewire_cli, cli);
+    furi_record_close("cli");
+#endif
+}
+
+void ibutton_cli_print_usage() {
+    printf("Usage:\r\n");
+    printf("ikey read\r\n");
+    printf("ikey emulate <key_type> <key_data>\r\n");
+    printf("ikey write Dallas <key_data>\r\n");
+    printf("\t<key_type> choose from:\r\n");
+    printf("\tDallas (8 bytes key_data)\r\n");
+    printf("\tCyfral (2 bytes key_data)\r\n");
+    printf("\tMetakom (4 bytes key_data)\r\n");
+    printf("\t<key_data> are hex-formatted\r\n");
+};
+
+bool ibutton_cli_get_key_type(string_t data, iButtonKeyType* type) {
+    bool result = false;
+
+    if(string_cmp_str(data, "Dallas") == 0 || string_cmp_str(data, "dallas") == 0) {
+        result = true;
+        *type = iButtonKeyDS1990;
+    } else if(string_cmp_str(data, "Cyfral") == 0 || string_cmp_str(data, "cyfral") == 0) {
+        result = true;
+        *type = iButtonKeyCyfral;
+    } else if(string_cmp_str(data, "Metakom") == 0 || string_cmp_str(data, "metakom") == 0) {
+        result = true;
+        *type = iButtonKeyMetakom;
+    }
+
+    return result;
+}
+
+void ibutton_cli_print_key_data(iButtonKey* key) {
+    const uint8_t* key_data = ibutton_key_get_data_p(key);
+    iButtonKeyType type = ibutton_key_get_type(key);
+
+    printf("%s ", ibutton_key_get_string_by_type(type));
+    for(size_t i = 0; i < ibutton_key_get_size_by_type(type); i++) {
+        printf("%02X", key_data[i]);
+    }
+
+    printf("\r\n");
+}
+
+#define EVENT_FLAG_IBUTTON_COMPLETE (1 << 0)
+
+static void ibutton_cli_worker_read_cb(void* context) {
+    osEventFlagsId_t event = context;
+    osEventFlagsSet(event, EVENT_FLAG_IBUTTON_COMPLETE);
+}
+
+void ibutton_cli_read(Cli* cli) {
+    iButtonKey* key = ibutton_key_alloc();
+    iButtonWorker* worker = ibutton_worker_alloc();
+    osEventFlagsId_t event = osEventFlagsNew(NULL);
+
+    ibutton_worker_start_thread(worker);
+    ibutton_worker_read_set_callback(worker, ibutton_cli_worker_read_cb, event);
+
+    ibutton_worker_read_start(worker, key);
+    while(true) {
+        uint32_t flags = osEventFlagsWait(event, EVENT_FLAG_IBUTTON_COMPLETE, osFlagsWaitAny, 100);
+
+        if(flags & EVENT_FLAG_IBUTTON_COMPLETE) {
+            ibutton_cli_print_key_data(key);
+
+            if(ibutton_key_get_type(key) == iButtonKeyDS1990) {
+                if(!ibutton_key_dallas_crc_is_valid(key)) {
+                    printf("Warning: invalid CRC\r\n");
+                }
+
+                if(!ibutton_key_dallas_is_1990_key(key)) {
+                    printf("Warning: not a key\r\n");
+                }
+            }
+            break;
+        }
+
+        if(cli_cmd_interrupt_received(cli)) break;
+    }
+    ibutton_worker_stop(worker);
+
+    ibutton_worker_stop_thread(worker);
+    ibutton_worker_free(worker);
+    ibutton_key_free(key);
+
+    osEventFlagsDelete(event);
+};
+
+typedef struct {
+    osEventFlagsId_t event;
+    iButtonWorkerWriteResult result;
+} iButtonWriteContext;
+
+static void ibutton_cli_worker_write_cb(void* context, iButtonWorkerWriteResult result) {
+    iButtonWriteContext* write_context = (iButtonWriteContext*)context;
+    write_context->result = result;
+    osEventFlagsSet(write_context->event, EVENT_FLAG_IBUTTON_COMPLETE);
+}
+
+void ibutton_cli_write(Cli* cli, string_t args) {
+    iButtonKey* key = ibutton_key_alloc();
+    iButtonWorker* worker = ibutton_worker_alloc();
+    iButtonKeyType type;
+    iButtonWriteContext write_context;
+    uint8_t key_data[IBUTTON_KEY_DATA_SIZE];
+    string_t data;
+
+    write_context.event = osEventFlagsNew(NULL);
+
+    string_init(data);
+    ibutton_worker_start_thread(worker);
+    ibutton_worker_write_set_callback(worker, ibutton_cli_worker_write_cb, &write_context);
+
+    do {
+        if(!args_read_string_and_trim(args, data)) {
+            ibutton_cli_print_usage();
+            break;
+        }
+
+        if(!ibutton_cli_get_key_type(data, &type)) {
+            ibutton_cli_print_usage();
+            break;
+        }
+
+        if(type != iButtonKeyDS1990) {
+            ibutton_cli_print_usage();
+            break;
+        }
+
+        if(!args_read_hex_bytes(args, key_data, ibutton_key_get_size_by_type(type))) {
+            ibutton_cli_print_usage();
+            break;
+        }
+
+        ibutton_key_set_type(key, type);
+        ibutton_key_set_data(key, key_data, ibutton_key_get_size_by_type(type));
+
+        printf("Writing key ");
+        ibutton_cli_print_key_data(key);
+        printf("Press Ctrl+C to abort\r\n");
+
+        ibutton_worker_write_start(worker, key);
+        while(true) {
+            uint32_t flags = osEventFlagsWait(
+                write_context.event, EVENT_FLAG_IBUTTON_COMPLETE, osFlagsWaitAny, 100);
+
+            if(flags & EVENT_FLAG_IBUTTON_COMPLETE) {
+                if(write_context.result == iButtonWorkerWriteSameKey ||
+                   write_context.result == iButtonWorkerWriteOK) {
+                    printf("Write success\r\n");
+                    break;
+                } else if(write_context.result == iButtonWorkerWriteCannotWrite) {
+                    printf("Write fail\r\n");
+                    break;
+                }
+            }
+
+            if(cli_cmd_interrupt_received(cli)) break;
+        }
+        ibutton_worker_stop(worker);
+    } while(false);
+
+    string_clear(data);
+    ibutton_worker_stop_thread(worker);
+    ibutton_worker_free(worker);
+    ibutton_key_free(key);
+
+    osEventFlagsDelete(write_context.event);
+};
+
+void ibutton_cli_emulate(Cli* cli, string_t args) {
+    iButtonKey* key = ibutton_key_alloc();
+    iButtonWorker* worker = ibutton_worker_alloc();
+    iButtonKeyType type;
+    uint8_t key_data[IBUTTON_KEY_DATA_SIZE];
+    string_t data;
+
+    string_init(data);
+    ibutton_worker_start_thread(worker);
+
+    do {
+        if(!args_read_string_and_trim(args, data)) {
+            ibutton_cli_print_usage();
+            break;
+        }
+
+        if(!ibutton_cli_get_key_type(data, &type)) {
+            ibutton_cli_print_usage();
+            break;
+        }
+
+        if(!args_read_hex_bytes(args, key_data, ibutton_key_get_size_by_type(type))) {
+            ibutton_cli_print_usage();
+            break;
+        }
+
+        ibutton_key_set_type(key, type);
+        ibutton_key_set_data(key, key_data, ibutton_key_get_size_by_type(type));
+
+        printf("Emulating key ");
+        ibutton_cli_print_key_data(key);
+        printf("Press Ctrl+C to abort\r\n");
+
+        ibutton_worker_emulate_start(worker, key);
+        while(!cli_cmd_interrupt_received(cli)) {
+            delay(100);
+        };
+        ibutton_worker_stop(worker);
+    } while(false);
+
+    string_clear(data);
+    ibutton_worker_stop_thread(worker);
+    ibutton_worker_free(worker);
+    ibutton_key_free(key);
+};
+
+void ibutton_cli(Cli* cli, string_t args, void* context) {
+    string_t cmd;
+    string_init(cmd);
+
+    if(!args_read_string_and_trim(args, cmd)) {
+        string_clear(cmd);
+        ibutton_cli_print_usage();
+        return;
+    }
+
+    if(string_cmp_str(cmd, "read") == 0) {
+        ibutton_cli_read(cli);
+    } else if(string_cmp_str(cmd, "write") == 0) {
+        ibutton_cli_write(cli, args);
+    } else if(string_cmp_str(cmd, "emulate") == 0) {
+        ibutton_cli_emulate(cli, args);
+    } else {
+        ibutton_cli_print_usage();
+    }
+
+    string_clear(cmd);
+}
+
+void onewire_cli_print_usage() {
+    printf("Usage:\r\n");
+    printf("onewire search\r\n");
+};
+
+void onewire_cli_search(Cli* cli) {
+    OneWireHost* onewire = onewire_host_alloc();
+    uint8_t address[8];
+    bool done = false;
+
+    printf("Search started\r\n");
+
+    onewire_host_start(onewire);
+    furi_hal_power_enable_otg();
+
+    while(!done) {
+        if(onewire_host_search(onewire, address, NORMAL_SEARCH) != 1) {
+            printf("Search finished\r\n");
+            onewire_host_reset_search(onewire);
+            done = true;
+        } else {
+            printf("Found: ");
+            for(uint8_t i = 0; i < 8; i++) {
+                printf("%02X", address[i]);
+            }
+            printf("\r\n");
+        }
+        delay(100);
+    }
+
+    furi_hal_power_disable_otg();
+    onewire_host_free(onewire);
+}
+
+void onewire_cli(Cli* cli, string_t args, void* context) {
+    string_t cmd;
+    string_init(cmd);
+
+    if(!args_read_string_and_trim(args, cmd)) {
+        string_clear(cmd);
+        onewire_cli_print_usage();
+        return;
+    }
+
+    if(string_cmp_str(cmd, "search") == 0) {
+        onewire_cli_search(cli);
+    }
+
+    string_clear(cmd);
+}

+ 0 - 292
applications/ibutton/ibutton_cli.cpp

@@ -1,292 +0,0 @@
-#include <furi.h>
-#include <furi_hal.h>
-#include <stdarg.h>
-#include <cli/cli.h>
-#include <lib/toolbox/args.h>
-
-#include "helpers/key_info.h"
-#include "helpers/key_worker.h"
-
-#include <memory>
-
-void ibutton_cli(Cli* cli, string_t args, void* context);
-void onewire_cli(Cli* cli, string_t args, void* context);
-
-// app cli function
-extern "C" void ibutton_on_system_start() {
-#ifdef SRV_CLI
-    Cli* cli = static_cast<Cli*>(furi_record_open("cli"));
-    cli_add_command(cli, "ikey", CliCommandFlagDefault, ibutton_cli, cli);
-    cli_add_command(cli, "onewire", CliCommandFlagDefault, onewire_cli, cli);
-    furi_record_close("cli");
-#endif
-}
-
-void ibutton_cli_print_usage() {
-    printf("Usage:\r\n");
-    printf("ikey read\r\n");
-    printf("ikey <write | emulate> <key_type> <key_data>\r\n");
-    printf("\t<key_type> choose from:\r\n");
-    printf("\tDallas (8 bytes key_data)\r\n");
-    printf("\tCyfral (2 bytes key_data)\r\n");
-    printf("\tMetakom (4 bytes key_data)\r\n");
-    printf("\t<key_data> are hex-formatted\r\n");
-};
-
-bool ibutton_cli_get_key_type(string_t data, iButtonKeyType* type) {
-    bool result = false;
-
-    if(string_cmp_str(data, "Dallas") == 0 || string_cmp_str(data, "dallas") == 0) {
-        result = true;
-        *type = iButtonKeyType::KeyDallas;
-    } else if(string_cmp_str(data, "Cyfral") == 0 || string_cmp_str(data, "cyfral") == 0) {
-        result = true;
-        *type = iButtonKeyType::KeyCyfral;
-    } else if(string_cmp_str(data, "Metakom") == 0 || string_cmp_str(data, "metakom") == 0) {
-        result = true;
-        *type = iButtonKeyType::KeyMetakom;
-    }
-
-    return result;
-}
-
-void ibutton_cli_print_key_data(iButtonKey* key) {
-    uint8_t* key_data = key->get_data();
-    switch(key->get_key_type()) {
-    case iButtonKeyType::KeyDallas:
-        printf(
-            "Dallas %02X%02X%02X%02X%02X%02X%02X%02X\r\n",
-            key_data[0],
-            key_data[1],
-            key_data[2],
-            key_data[3],
-            key_data[4],
-            key_data[5],
-            key_data[6],
-            key_data[7]);
-        break;
-    case iButtonKeyType::KeyCyfral:
-        printf("Cyfral %02X%02X\r\n", key_data[0], key_data[1]);
-        break;
-    case iButtonKeyType::KeyMetakom:
-        printf("Metakom %02X%02X%02X%02X\r\n", key_data[0], key_data[1], key_data[2], key_data[3]);
-        break;
-    }
-}
-
-void ibutton_cli_read(Cli* cli) {
-    iButtonKey key;
-    std::unique_ptr<KeyWorker> worker(new KeyWorker(&ibutton_gpio));
-
-    bool exit = false;
-
-    worker->start_read();
-    printf("Reading iButton...\r\nPress Ctrl+C to abort\r\n");
-
-    while(!exit) {
-        exit = cli_cmd_interrupt_received(cli);
-
-        switch(worker->read(&key)) {
-        case KeyReader::Error::EMPTY:
-            break;
-        case KeyReader::Error::CRC_ERROR:
-            ibutton_cli_print_key_data(&key);
-            printf("Warning: invalid CRC\r\n");
-            exit = true;
-            break;
-        case KeyReader::Error::OK:
-            ibutton_cli_print_key_data(&key);
-            exit = true;
-            break;
-        case KeyReader::Error::NOT_ARE_KEY:
-            ibutton_cli_print_key_data(&key);
-            printf("Warning: not a key\r\n");
-            exit = true;
-            break;
-        }
-
-        delay(100);
-    }
-
-    worker->stop_read();
-};
-
-void ibutton_cli_write(Cli* cli, string_t args) {
-    iButtonKey key;
-    iButtonKeyType type;
-    std::unique_ptr<KeyWorker> worker(new KeyWorker(&ibutton_gpio));
-
-    bool exit = false;
-    string_t data;
-    string_init(data);
-
-    if(!args_read_string_and_trim(args, data)) {
-        ibutton_cli_print_usage();
-        string_clear(data);
-        return;
-    }
-
-    if(!ibutton_cli_get_key_type(data, &type)) {
-        ibutton_cli_print_usage();
-        string_clear(data);
-        return;
-    }
-
-    key.set_type(type);
-
-    if(!args_read_hex_bytes(args, key.get_data(), key.get_type_data_size())) {
-        ibutton_cli_print_usage();
-        string_clear(data);
-        return;
-    }
-
-    printf("Writing key ");
-    ibutton_cli_print_key_data(&key);
-    printf("Press Ctrl+C to abort\r\n");
-
-    worker->start_write();
-
-    while(!exit) {
-        exit = cli_cmd_interrupt_received(cli);
-
-        KeyWriter::Error result = worker->write(&key);
-
-        switch(result) {
-        case KeyWriter::Error::SAME_KEY:
-        case KeyWriter::Error::OK:
-            printf("Write success\r\n");
-            exit = true;
-            break;
-        case KeyWriter::Error::NO_DETECT:
-            break;
-        case KeyWriter::Error::CANNOT_WRITE:
-            printf("Write fail\r\n");
-            exit = true;
-            break;
-        }
-
-        delay(100);
-    };
-
-    worker->stop_write();
-
-    string_clear(data);
-};
-
-void ibutton_cli_emulate(Cli* cli, string_t args) {
-    iButtonKey key;
-    iButtonKeyType type;
-    std::unique_ptr<KeyWorker> worker(new KeyWorker(&ibutton_gpio));
-    bool exit = false;
-    string_t data;
-    string_init(data);
-
-    if(!args_read_string_and_trim(args, data)) {
-        ibutton_cli_print_usage();
-        string_clear(data);
-        return;
-    }
-
-    if(!ibutton_cli_get_key_type(data, &type)) {
-        ibutton_cli_print_usage();
-        string_clear(data);
-        return;
-    }
-
-    key.set_type(type);
-
-    if(!args_read_hex_bytes(args, key.get_data(), key.get_type_data_size())) {
-        ibutton_cli_print_usage();
-        string_clear(data);
-        return;
-    }
-
-    printf("Emulating key ");
-    ibutton_cli_print_key_data(&key);
-    printf("Press Ctrl+C to abort\r\n");
-
-    worker->start_emulate(&key);
-
-    while(!exit) {
-        exit = cli_cmd_interrupt_received(cli);
-        delay(100);
-    };
-
-    worker->stop_emulate();
-
-    string_clear(data);
-};
-
-void ibutton_cli(Cli* cli, string_t args, void* context) {
-    string_t cmd;
-    string_init(cmd);
-
-    if(!args_read_string_and_trim(args, cmd)) {
-        string_clear(cmd);
-        ibutton_cli_print_usage();
-        return;
-    }
-
-    if(string_cmp_str(cmd, "read") == 0) {
-        ibutton_cli_read(cli);
-    } else if(string_cmp_str(cmd, "write") == 0) {
-        ibutton_cli_write(cli, args);
-    } else if(string_cmp_str(cmd, "emulate") == 0) {
-        ibutton_cli_emulate(cli, args);
-    } else {
-        ibutton_cli_print_usage();
-    }
-
-    string_clear(cmd);
-}
-
-void onewire_cli_print_usage() {
-    printf("Usage:\r\n");
-    printf("onewire search\r\n");
-};
-
-void onewire_cli_search(Cli* cli) {
-    OneWireMaster onewire(&ibutton_gpio);
-    uint8_t address[8];
-    bool done = false;
-
-    printf("Search started\r\n");
-
-    onewire.start();
-    furi_hal_power_enable_otg();
-
-    while(!done) {
-        if(onewire.search(address, true) != 1) {
-            printf("Search finished\r\n");
-            onewire.reset_search();
-            done = true;
-        } else {
-            printf("Found: ");
-            for(uint8_t i = 0; i < 8; i++) {
-                printf("%02X", address[i]);
-            }
-            printf("\r\n");
-        }
-        delay(100);
-    }
-
-    furi_hal_power_disable_otg();
-    onewire.stop();
-}
-
-void onewire_cli(Cli* cli, string_t args, void* context) {
-    string_t cmd;
-    string_init(cmd);
-
-    if(!args_read_string_and_trim(args, cmd)) {
-        string_clear(cmd);
-        onewire_cli_print_usage();
-        return;
-    }
-
-    if(string_cmp_str(cmd, "search") == 0) {
-        onewire_cli_search(cli);
-    }
-
-    string_clear(cmd);
-}

+ 5 - 0
applications/ibutton/ibutton_event.h

@@ -2,6 +2,7 @@
 #include <stdint.h>
 #include <gui/modules/dialog_ex.h>
 #include <gui/modules/widget.h>
+#include <one_wire/ibutton/ibutton_worker.h>
 
 class iButtonApp;
 
@@ -16,6 +17,9 @@ public:
         EventTypeTextEditResult,
         EventTypeByteEditResult,
         EventTypeWidgetButtonResult,
+        EventTypeWorkerEmulated,
+        EventTypeWorkerRead,
+        EventTypeWorkerWrite,
     };
 
     // payload
@@ -23,6 +27,7 @@ public:
         uint32_t menu_index;
         DialogExResult dialog_result;
         GuiButtonType widget_button_result;
+        iButtonWorkerWriteResult worker_write_result;
     } payload;
 
     // event type

+ 0 - 95
applications/ibutton/ibutton_key.cpp

@@ -1,95 +0,0 @@
-#include "ibutton_key.h"
-#include <furi.h>
-
-uint8_t iButtonKey::get_size() {
-    return IBUTTON_KEY_DATA_SIZE;
-}
-
-void iButtonKey::set_data(uint8_t* _data, uint8_t _data_count) {
-    furi_check(_data_count > 0);
-    furi_check(_data_count <= get_size());
-
-    memset(data, 0, get_size());
-    memcpy(data, _data, _data_count);
-}
-
-void iButtonKey::clear_data() {
-    memset(data, 0, get_size());
-}
-
-uint8_t* iButtonKey::get_data() {
-    return data;
-}
-
-uint8_t iButtonKey::get_type_data_size() {
-    return get_type_data_size_by_type(type);
-}
-
-void iButtonKey::set_name(const char* _name) {
-    strlcpy(name, _name, IBUTTON_KEY_NAME_SIZE);
-}
-
-char* iButtonKey::get_name() {
-    return name;
-}
-
-void iButtonKey::set_type(iButtonKeyType _key_type) {
-    type = _key_type;
-}
-
-iButtonKeyType iButtonKey::get_key_type() {
-    return type;
-}
-
-const char* iButtonKey::get_key_type_string_by_type(iButtonKeyType key_type) {
-    switch(key_type) {
-    case iButtonKeyType::KeyCyfral:
-        return "Cyfral";
-        break;
-    case iButtonKeyType::KeyMetakom:
-        return "Metakom";
-        break;
-    case iButtonKeyType::KeyDallas:
-        return "Dallas";
-        break;
-    default:
-        furi_crash("Invalid iButton type");
-        return "";
-        break;
-    }
-}
-
-bool iButtonKey::get_key_type_by_type_string(const char* type_string, iButtonKeyType* key_type) {
-    if(strcmp(type_string, get_key_type_string_by_type(iButtonKeyType::KeyCyfral)) == 0) {
-        *key_type = iButtonKeyType::KeyCyfral;
-    } else if(strcmp(type_string, get_key_type_string_by_type(iButtonKeyType::KeyMetakom)) == 0) {
-        *key_type = iButtonKeyType::KeyMetakom;
-    } else if(strcmp(type_string, get_key_type_string_by_type(iButtonKeyType::KeyDallas)) == 0) {
-        *key_type = iButtonKeyType::KeyDallas;
-    } else {
-        return false;
-    }
-
-    return true;
-}
-
-uint8_t iButtonKey::get_type_data_size_by_type(iButtonKeyType key_type) {
-    uint8_t size = 0;
-
-    switch(key_type) {
-    case iButtonKeyType::KeyCyfral:
-        size = 2;
-        break;
-    case iButtonKeyType::KeyMetakom:
-        size = 4;
-        break;
-    case iButtonKeyType::KeyDallas:
-        size = 8;
-        break;
-    }
-
-    return size;
-}
-
-iButtonKey::iButtonKey() {
-}

+ 0 - 31
applications/ibutton/ibutton_key.h

@@ -1,31 +0,0 @@
-#pragma once
-#include <stdint.h>
-#include "helpers/key_info.h"
-
-class iButtonKey {
-public:
-    uint8_t get_size();
-
-    void set_data(uint8_t* data, uint8_t data_count);
-    void clear_data();
-    uint8_t* get_data();
-    uint8_t get_type_data_size();
-
-    void set_name(const char* name);
-    char* get_name();
-
-    void set_type(iButtonKeyType key_type);
-    iButtonKeyType get_key_type();
-
-    const char* get_key_type_string_by_type(iButtonKeyType key_type);
-    bool get_key_type_by_type_string(const char* type_string, iButtonKeyType* key_type);
-    uint8_t get_type_data_size_by_type(iButtonKeyType key_type);
-
-    iButtonKey();
-
-private:
-    uint8_t data[IBUTTON_KEY_DATA_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0};
-    char name[IBUTTON_KEY_NAME_SIZE] = {0};
-
-    iButtonKeyType type = iButtonKeyType::KeyDallas;
-};

+ 20 - 21
applications/ibutton/scene/ibutton_scene_add_type.cpp

@@ -1,7 +1,5 @@
 #include "ibutton_scene_add_type.h"
 #include "../ibutton_app.h"
-#include "../ibutton_view_manager.h"
-#include "../ibutton_event.h"
 #include <callback-connector.h>
 
 typedef enum {
@@ -10,14 +8,23 @@ typedef enum {
     SubmenuIndexMetakom,
 } SubmenuIndex;
 
+static void submenu_callback(void* context, uint32_t index) {
+    iButtonApp* app = static_cast<iButtonApp*>(context);
+    iButtonEvent event;
+
+    event.type = iButtonEvent::Type::EventTypeMenuSelected;
+    event.payload.menu_index = index;
+
+    app->get_view_manager()->send_event(&event);
+}
+
 void iButtonSceneAddType::on_enter(iButtonApp* app) {
     iButtonAppViewManager* view_manager = app->get_view_manager();
     Submenu* submenu = view_manager->get_submenu();
-    auto callback = cbc::obtain_connector(this, &iButtonSceneAddType::submenu_callback);
 
-    submenu_add_item(submenu, "Cyfral", SubmenuIndexCyfral, callback, app);
-    submenu_add_item(submenu, "Dallas", SubmenuIndexDallas, callback, app);
-    submenu_add_item(submenu, "Metakom", SubmenuIndexMetakom, callback, app);
+    submenu_add_item(submenu, "Cyfral", SubmenuIndexCyfral, submenu_callback, app);
+    submenu_add_item(submenu, "Dallas", SubmenuIndexDallas, submenu_callback, app);
+    submenu_add_item(submenu, "Metakom", SubmenuIndexMetakom, submenu_callback, app);
     submenu_set_selected_item(submenu, submenu_item_selected);
 
     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu);
@@ -28,19 +35,21 @@ bool iButtonSceneAddType::on_event(iButtonApp* app, iButtonEvent* event) {
 
     if(event->type == iButtonEvent::Type::EventTypeMenuSelected) {
         submenu_item_selected = event->payload.menu_index;
+        iButtonKey* key = app->get_key();
+
         switch(event->payload.menu_index) {
         case SubmenuIndexCyfral:
-            app->get_key()->set_type(iButtonKeyType::KeyCyfral);
+            ibutton_key_set_type(key, iButtonKeyCyfral);
             break;
         case SubmenuIndexDallas:
-            app->get_key()->set_type(iButtonKeyType::KeyDallas);
+            ibutton_key_set_type(key, iButtonKeyDS1990);
             break;
         case SubmenuIndexMetakom:
-            app->get_key()->set_type(iButtonKeyType::KeyMetakom);
+            ibutton_key_set_type(key, iButtonKeyMetakom);
             break;
         }
-        app->get_key()->set_name("");
-        app->get_key()->clear_data();
+        ibutton_key_set_name(key, "");
+        ibutton_key_clear_data(key);
         app->switch_to_next_scene(iButtonApp::Scene::SceneAddValue);
         consumed = true;
     }
@@ -54,13 +63,3 @@ void iButtonSceneAddType::on_exit(iButtonApp* app) {
 
     submenu_reset(submenu);
 }
-
-void iButtonSceneAddType::submenu_callback(void* context, uint32_t index) {
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-
-    event.type = iButtonEvent::Type::EventTypeMenuSelected;
-    event.payload.menu_index = index;
-
-    app->get_view_manager()->send_event(&event);
-}

+ 0 - 1
applications/ibutton/scene/ibutton_scene_add_type.h

@@ -8,6 +8,5 @@ public:
     void on_exit(iButtonApp* app) final;
 
 private:
-    void submenu_callback(void* context, uint32_t index);
     uint32_t submenu_item_selected = 0;
 };

+ 21 - 16
applications/ibutton/scene/ibutton_scene_add_value.cpp

@@ -1,17 +1,30 @@
 #include "ibutton_scene_add_value.h"
 #include "../ibutton_app.h"
-#include "../ibutton_view_manager.h"
-#include "../ibutton_event.h"
-#include <callback-connector.h>
 #include <dolphin/dolphin.h>
 
+static void byte_input_callback(void* context) {
+    iButtonApp* app = static_cast<iButtonApp*>(context);
+    iButtonEvent event;
+
+    event.type = iButtonEvent::Type::EventTypeByteEditResult;
+    app->get_view_manager()->send_event(&event);
+}
+
 void iButtonSceneAddValue::on_enter(iButtonApp* app) {
     iButtonAppViewManager* view_manager = app->get_view_manager();
     ByteInput* byte_input = view_manager->get_byte_input();
-    auto callback = cbc::obtain_connector(this, &iButtonSceneAddValue::byte_input_callback);
-    memcpy(this->new_key_data, app->get_key()->get_data(), app->get_key()->get_type_data_size());
+    iButtonKey* key = app->get_key();
+
+    memcpy(this->new_key_data, ibutton_key_get_data_p(key), ibutton_key_get_data_size(key));
+
     byte_input_set_result_callback(
-        byte_input, callback, NULL, app, this->new_key_data, app->get_key()->get_type_data_size());
+        byte_input,
+        byte_input_callback,
+        NULL,
+        app,
+        this->new_key_data,
+        ibutton_key_get_data_size(key));
+
     byte_input_set_header_text(byte_input, "Enter the key");
 
     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewByteInput);
@@ -21,6 +34,7 @@ bool iButtonSceneAddValue::on_event(iButtonApp* app, iButtonEvent* event) {
     bool consumed = false;
 
     if(event->type == iButtonEvent::Type::EventTypeByteEditResult) {
+        ibutton_key_set_data(app->get_key(), this->new_key_data, IBUTTON_KEY_DATA_SIZE);
         DOLPHIN_DEED(DolphinDeedIbuttonAdd);
         app->switch_to_next_scene(iButtonApp::Scene::SceneSaveName);
         consumed = true;
@@ -35,13 +49,4 @@ void iButtonSceneAddValue::on_exit(iButtonApp* app) {
 
     byte_input_set_result_callback(byte_input, NULL, NULL, NULL, NULL, 0);
     byte_input_set_header_text(byte_input, {0});
-}
-
-void iButtonSceneAddValue::byte_input_callback(void* context) {
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-
-    event.type = iButtonEvent::Type::EventTypeByteEditResult;
-    memcpy(app->get_key()->get_data(), this->new_key_data, app->get_key()->get_type_data_size());
-    app->get_view_manager()->send_event(&event);
-}
+}

+ 1 - 2
applications/ibutton/scene/ibutton_scene_add_value.h

@@ -1,6 +1,6 @@
 #pragma once
 #include "ibutton_scene_generic.h"
-#include "../ibutton_key.h"
+#include <one_wire/ibutton/ibutton_key.h>
 
 class iButtonSceneAddValue : public iButtonScene {
 public:
@@ -9,6 +9,5 @@ public:
     void on_exit(iButtonApp* app) final;
 
 private:
-    void byte_input_callback(void* context);
     uint8_t new_key_data[IBUTTON_KEY_DATA_SIZE] = {};
 };

+ 19 - 28
applications/ibutton/scene/ibutton_scene_delete_confirm.cpp

@@ -1,25 +1,31 @@
 #include "ibutton_scene_delete_confirm.h"
 #include "../ibutton_app.h"
-#include "../ibutton_view_manager.h"
-#include "../ibutton_event.h"
-#include <callback-connector.h>
+
+static void widget_callback(GuiButtonType result, InputType type, void* context) {
+    iButtonApp* app = static_cast<iButtonApp*>(context);
+    iButtonEvent event;
+
+    if(type == InputTypeShort) {
+        event.type = iButtonEvent::Type::EventTypeWidgetButtonResult;
+        event.payload.widget_button_result = result;
+        app->get_view_manager()->send_event(&event);
+    }
+}
 
 void iButtonSceneDeleteConfirm::on_enter(iButtonApp* app) {
     iButtonAppViewManager* view_manager = app->get_view_manager();
     Widget* widget = view_manager->get_widget();
-    auto callback = cbc::obtain_connector(this, &iButtonSceneDeleteConfirm::widget_callback);
-
     iButtonKey* key = app->get_key();
-    uint8_t* key_data = key->get_data();
+    const uint8_t* key_data = ibutton_key_get_data_p(key);
 
-    app->set_text_store("\e#Delete %s?\e#", key->get_name());
+    app->set_text_store("\e#Delete %s?\e#", ibutton_key_get_name_p(key));
     widget_add_text_box_element(
         widget, 0, 0, 128, 27, AlignCenter, AlignCenter, app->get_text_store());
-    widget_add_button_element(widget, GuiButtonTypeLeft, "Back", callback, app);
-    widget_add_button_element(widget, GuiButtonTypeRight, "Delete", callback, app);
+    widget_add_button_element(widget, GuiButtonTypeLeft, "Back", widget_callback, app);
+    widget_add_button_element(widget, GuiButtonTypeRight, "Delete", widget_callback, app);
 
-    switch(key->get_key_type()) {
-    case iButtonKeyType::KeyDallas:
+    switch(ibutton_key_get_type(key)) {
+    case iButtonKeyDS1990:
         app->set_text_store(
             "%02X %02X %02X %02X %02X %02X %02X %02X",
             key_data[0],
@@ -33,12 +39,12 @@ void iButtonSceneDeleteConfirm::on_enter(iButtonApp* app) {
         widget_add_string_element(
             widget, 64, 45, AlignCenter, AlignBottom, FontSecondary, "Dallas");
         break;
-    case iButtonKeyType::KeyCyfral:
+    case iButtonKeyCyfral:
         app->set_text_store("%02X %02X", key_data[0], key_data[1]);
         widget_add_string_element(
             widget, 64, 45, AlignCenter, AlignBottom, FontSecondary, "Cyfral");
         break;
-    case iButtonKeyType::KeyMetakom:
+    case iButtonKeyMetakom:
         app->set_text_store(
             "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]);
         widget_add_string_element(
@@ -77,18 +83,3 @@ void iButtonSceneDeleteConfirm::on_exit(iButtonApp* app) {
 
     widget_reset(widget);
 }
-
-void iButtonSceneDeleteConfirm::widget_callback(
-    GuiButtonType result,
-    InputType type,
-    void* context) {
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-
-    if(type == InputTypeShort) {
-        event.type = iButtonEvent::Type::EventTypeWidgetButtonResult;
-        event.payload.widget_button_result = result;
-    }
-
-    app->get_view_manager()->send_event(&event);
-}

+ 0 - 3
applications/ibutton/scene/ibutton_scene_delete_confirm.h

@@ -6,7 +6,4 @@ public:
     void on_enter(iButtonApp* app) final;
     bool on_event(iButtonApp* app, iButtonEvent* event) final;
     void on_exit(iButtonApp* app) final;
-
-private:
-    void widget_callback(GuiButtonType result, InputType type, void* context);
 };

+ 8 - 13
applications/ibutton/scene/ibutton_scene_delete_success.cpp

@@ -1,19 +1,21 @@
 #include "ibutton_scene_delete_success.h"
 #include "../ibutton_app.h"
-#include "../ibutton_view_manager.h"
-#include "../ibutton_event.h"
-#include "../ibutton_key.h"
-#include <callback-connector.h>
+
+static void popup_callback(void* context) {
+    iButtonApp* app = static_cast<iButtonApp*>(context);
+    iButtonEvent event;
+    event.type = iButtonEvent::Type::EventTypeBack;
+    app->get_view_manager()->send_event(&event);
+}
 
 void iButtonSceneDeleteSuccess::on_enter(iButtonApp* app) {
     iButtonAppViewManager* view_manager = app->get_view_manager();
     Popup* popup = view_manager->get_popup();
-    auto callback = cbc::obtain_connector(this, &iButtonSceneDeleteSuccess::popup_callback);
 
     popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62);
     popup_set_text(popup, "Deleted", 83, 19, AlignLeft, AlignBottom);
 
-    popup_set_callback(popup, callback);
+    popup_set_callback(popup, popup_callback);
     popup_set_context(popup, app);
     popup_set_timeout(popup, 1500);
     popup_enable_timeout(popup);
@@ -41,11 +43,4 @@ void iButtonSceneDeleteSuccess::on_exit(iButtonApp* app) {
     popup_disable_timeout(popup);
     popup_set_context(popup, NULL);
     popup_set_callback(popup, NULL);
-}
-
-void iButtonSceneDeleteSuccess::popup_callback(void* context) {
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-    event.type = iButtonEvent::Type::EventTypeBack;
-    app->get_view_manager()->send_event(&event);
 }

+ 0 - 3
applications/ibutton/scene/ibutton_scene_delete_success.h

@@ -6,7 +6,4 @@ public:
     void on_enter(iButtonApp* app) final;
     bool on_event(iButtonApp* app, iButtonEvent* event) final;
     void on_exit(iButtonApp* app) final;
-
-private:
-    void popup_callback(void* context);
 };

+ 23 - 20
applications/ibutton/scene/ibutton_scene_emulate.cpp

@@ -1,17 +1,21 @@
 #include "ibutton_scene_emulate.h"
 #include "../ibutton_app.h"
-#include "../ibutton_view_manager.h"
-#include "../ibutton_event.h"
-#include "../ibutton_key.h"
 #include <dolphin/dolphin.h>
-#include <callback-connector.h>
+
+static void emulate_callback(void* context, bool emulated) {
+    if(emulated) {
+        iButtonApp* app = static_cast<iButtonApp*>(context);
+        iButtonEvent event = {.type = iButtonEvent::Type::EventTypeWorkerEmulated};
+        app->get_view_manager()->send_event(&event);
+    }
+}
 
 void iButtonSceneEmulate::on_enter(iButtonApp* app) {
     iButtonAppViewManager* view_manager = app->get_view_manager();
     Popup* popup = view_manager->get_popup();
     iButtonKey* key = app->get_key();
-    uint8_t* key_data = key->get_data();
-    const char* key_name = key->get_name();
+    const uint8_t* key_data = ibutton_key_get_data_p(key);
+    const char* key_name = ibutton_key_get_name_p(key);
     uint8_t line_count = 2;
     DOLPHIN_DEED(DolphinDeedIbuttonEmulate);
 
@@ -21,8 +25,8 @@ void iButtonSceneEmulate::on_enter(iButtonApp* app) {
         line_count = 2;
     } else {
         // if not, show key data
-        switch(key->get_key_type()) {
-        case iButtonKeyType::KeyDallas:
+        switch(ibutton_key_get_type(key)) {
+        case iButtonKeyDS1990:
             app->set_text_store(
                 "emulating\n%02X %02X %02X %02X\n%02X %02X %02X %02X",
                 key_data[0],
@@ -35,11 +39,11 @@ void iButtonSceneEmulate::on_enter(iButtonApp* app) {
                 key_data[7]);
             line_count = 3;
             break;
-        case iButtonKeyType::KeyCyfral:
+        case iButtonKeyCyfral:
             app->set_text_store("emulating\n%02X %02X", key_data[0], key_data[1]);
             line_count = 2;
             break;
-        case iButtonKeyType::KeyMetakom:
+        case iButtonKeyMetakom:
             app->set_text_store(
                 "emulating\n%02X %02X %02X %02X",
                 key_data[0],
@@ -66,29 +70,28 @@ void iButtonSceneEmulate::on_enter(iButtonApp* app) {
     popup_set_icon(popup, 2, 10, &I_iButtonKey_49x44);
 
     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewPopup);
-    app->get_key_worker()->start_emulate(app->get_key());
+
+    ibutton_worker_emulate_set_callback(app->get_key_worker(), emulate_callback, app);
+    ibutton_worker_emulate_start(app->get_key_worker(), key);
 }
 
 bool iButtonSceneEmulate::on_event(iButtonApp* app, iButtonEvent* event) {
     bool consumed = false;
 
-    if(event->type == iButtonEvent::Type::EventTypeTick) {
+    if(event->type == iButtonEvent::Type::EventTypeWorkerEmulated) {
+        app->notify_yellow_blink();
+        consumed = true;
+    } else if(event->type == iButtonEvent::Type::EventTypeTick) {
+        app->notify_red_blink();
         consumed = true;
-        if(app->get_key_worker()->emulated()) {
-            app->notify_yellow_blink();
-        } else {
-            app->notify_red_blink();
-        }
     }
 
     return consumed;
 }
 
 void iButtonSceneEmulate::on_exit(iButtonApp* app) {
-    app->get_key_worker()->stop_emulate();
-
     Popup* popup = app->get_view_manager()->get_popup();
-
+    ibutton_worker_stop(app->get_key_worker());
     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
     popup_set_icon(popup, 0, 0, NULL);

+ 0 - 1
applications/ibutton/scene/ibutton_scene_emulate.h

@@ -1,6 +1,5 @@
 #pragma once
 #include "ibutton_scene_generic.h"
-#include "../helpers/key_emulator.h"
 
 class iButtonSceneEmulate : public iButtonScene {
 public:

+ 18 - 24
applications/ibutton/scene/ibutton_scene_info.cpp

@@ -1,24 +1,30 @@
 #include "ibutton_scene_info.h"
 #include "../ibutton_app.h"
-#include "../ibutton_view_manager.h"
-#include "../ibutton_event.h"
-#include <callback-connector.h>
+
+static void widget_callback(GuiButtonType result, InputType type, void* context) {
+    iButtonApp* app = static_cast<iButtonApp*>(context);
+    iButtonEvent event;
+
+    if(type == InputTypeShort) {
+        event.type = iButtonEvent::Type::EventTypeWidgetButtonResult;
+        event.payload.widget_button_result = result;
+        app->get_view_manager()->send_event(&event);
+    }
+}
 
 void iButtonSceneInfo::on_enter(iButtonApp* app) {
     iButtonAppViewManager* view_manager = app->get_view_manager();
     Widget* widget = view_manager->get_widget();
-    auto callback = cbc::obtain_connector(this, &iButtonSceneInfo::widget_callback);
-
     iButtonKey* key = app->get_key();
-    uint8_t* key_data = key->get_data();
+    const uint8_t* key_data = ibutton_key_get_data_p(key);
 
-    app->set_text_store("%s", key->get_name());
+    app->set_text_store("%s", ibutton_key_get_name_p(key));
     widget_add_text_box_element(
         widget, 0, 0, 128, 27, AlignCenter, AlignCenter, app->get_text_store());
-    widget_add_button_element(widget, GuiButtonTypeLeft, "Back", callback, app);
+    widget_add_button_element(widget, GuiButtonTypeLeft, "Back", widget_callback, app);
 
-    switch(key->get_key_type()) {
-    case iButtonKeyType::KeyDallas:
+    switch(ibutton_key_get_type(key)) {
+    case iButtonKeyDS1990:
         app->set_text_store(
             "%02X %02X %02X %02X %02X %02X %02X %02X",
             key_data[0],
@@ -32,13 +38,13 @@ void iButtonSceneInfo::on_enter(iButtonApp* app) {
         widget_add_string_element(
             widget, 64, 45, AlignCenter, AlignBottom, FontSecondary, "Dallas");
         break;
-    case iButtonKeyType::KeyMetakom:
+    case iButtonKeyMetakom:
         app->set_text_store(
             "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]);
         widget_add_string_element(
             widget, 64, 45, AlignCenter, AlignBottom, FontSecondary, "Metakom");
         break;
-    case iButtonKeyType::KeyCyfral:
+    case iButtonKeyCyfral:
         app->set_text_store("%02X %02X", key_data[0], key_data[1]);
         widget_add_string_element(
             widget, 64, 45, AlignCenter, AlignBottom, FontSecondary, "Cyfral");
@@ -71,15 +77,3 @@ void iButtonSceneInfo::on_exit(iButtonApp* app) {
 
     widget_reset(widget);
 }
-
-void iButtonSceneInfo::widget_callback(GuiButtonType result, InputType type, void* context) {
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-
-    if(type == InputTypeShort) {
-        event.type = iButtonEvent::Type::EventTypeWidgetButtonResult;
-        event.payload.widget_button_result = result;
-    }
-
-    app->get_view_manager()->send_event(&event);
-}

+ 0 - 3
applications/ibutton/scene/ibutton_scene_info.h

@@ -6,7 +6,4 @@ public:
     void on_enter(iButtonApp* app) final;
     bool on_event(iButtonApp* app, iButtonEvent* event) final;
     void on_exit(iButtonApp* app) final;
-
-private:
-    void widget_callback(GuiButtonType result, InputType type, void* context);
 };

+ 26 - 20
applications/ibutton/scene/ibutton_scene_read.cpp

@@ -1,12 +1,18 @@
 #include "ibutton_scene_read.h"
 #include "../ibutton_app.h"
-#include "../ibutton_view_manager.h"
-#include "../ibutton_event.h"
 #include <dolphin/dolphin.h>
 
+static void read_callback(void* context) {
+    iButtonApp* app = static_cast<iButtonApp*>(context);
+    iButtonEvent event = {.type = iButtonEvent::Type::EventTypeWorkerRead};
+    app->get_view_manager()->send_event(&event);
+}
+
 void iButtonSceneRead::on_enter(iButtonApp* app) {
     iButtonAppViewManager* view_manager = app->get_view_manager();
     Popup* popup = view_manager->get_popup();
+    iButtonKey* key = app->get_key();
+    iButtonWorker* worker = app->get_key_worker();
     DOLPHIN_DEED(DolphinDeedIbuttonRead);
 
     popup_set_header(popup, "iButton", 95, 26, AlignCenter, AlignBottom);
@@ -14,41 +20,41 @@ void iButtonSceneRead::on_enter(iButtonApp* app) {
     popup_set_icon(popup, 0, 5, &I_DolphinWait_61x59);
 
     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewPopup);
-    app->get_key()->set_name("");
+    ibutton_key_set_name(key, "");
 
-    app->get_key_worker()->start_read();
+    ibutton_worker_read_set_callback(worker, read_callback, app);
+    ibutton_worker_read_start(worker, key);
 }
 
 bool iButtonSceneRead::on_event(iButtonApp* app, iButtonEvent* event) {
     bool consumed = false;
 
-    if(event->type == iButtonEvent::Type::EventTypeTick) {
+    if(event->type == iButtonEvent::Type::EventTypeWorkerRead) {
         consumed = true;
-        app->notify_red_blink();
 
-        switch(app->get_key_worker()->read(app->get_key())) {
-        case KeyReader::Error::EMPTY:
-            break;
-        case KeyReader::Error::OK:
+        iButtonKey* key = app->get_key();
+        if(ibutton_key_get_type(key) == iButtonKeyDS1990) {
+            if(!ibutton_key_dallas_crc_is_valid(key)) {
+                app->switch_to_next_scene(iButtonApp::Scene::SceneReadCRCError);
+            } else if(!ibutton_key_dallas_is_1990_key(key)) {
+                app->switch_to_next_scene(iButtonApp::Scene::SceneReadNotKeyError);
+            } else {
+                app->switch_to_next_scene(iButtonApp::Scene::SceneReadSuccess);
+            }
+        } else {
             app->switch_to_next_scene(iButtonApp::Scene::SceneReadSuccess);
-            break;
-        case KeyReader::Error::CRC_ERROR:
-            app->switch_to_next_scene(iButtonApp::Scene::SceneReadCRCError);
-            break;
-        case KeyReader::Error::NOT_ARE_KEY:
-            app->switch_to_next_scene(iButtonApp::Scene::SceneReadNotKeyError);
-            break;
         }
+    } else if(event->type == iButtonEvent::Type::EventTypeTick) {
+        consumed = true;
+        app->notify_red_blink();
     }
 
     return consumed;
 }
 
 void iButtonSceneRead::on_exit(iButtonApp* app) {
-    app->get_key_worker()->stop_read();
-
     Popup* popup = app->get_view_manager()->get_popup();
-
+    ibutton_worker_stop(app->get_key_worker());
     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
     popup_set_icon(popup, 0, 0, NULL);

+ 0 - 2
applications/ibutton/scene/ibutton_scene_read.h

@@ -6,6 +6,4 @@ public:
     void on_enter(iButtonApp* app) final;
     bool on_event(iButtonApp* app, iButtonEvent* event) final;
     void on_exit(iButtonApp* app) final;
-
-private:
 };

+ 14 - 19
applications/ibutton/scene/ibutton_scene_read_crc_error.cpp

@@ -1,16 +1,21 @@
 #include "ibutton_scene_read_crc_error.h"
 #include "../ibutton_app.h"
-#include "../ibutton_view_manager.h"
-#include "../ibutton_event.h"
-#include <callback-connector.h>
+#include <one_wire/maxim_crc.h>
+
+static void dialog_ex_callback(DialogExResult result, void* context) {
+    iButtonApp* app = static_cast<iButtonApp*>(context);
+    iButtonEvent event;
+
+    event.type = iButtonEvent::Type::EventTypeDialogResult;
+    event.payload.dialog_result = result;
+
+    app->get_view_manager()->send_event(&event);
+}
 
 void iButtonSceneReadCRCError::on_enter(iButtonApp* app) {
     iButtonAppViewManager* view_manager = app->get_view_manager();
     DialogEx* dialog_ex = view_manager->get_dialog_ex();
-    auto callback = cbc::obtain_connector(this, &iButtonSceneReadCRCError::dialog_ex_callback);
-
-    iButtonKey* key = app->get_key();
-    uint8_t* key_data = key->get_data();
+    const uint8_t* key_data = ibutton_key_get_data_p(app->get_key());
 
     app->set_text_store(
         "%02X %02X %02X %02X %02X %02X %02X %02X\nExpected CRC: %X",
@@ -22,13 +27,13 @@ void iButtonSceneReadCRCError::on_enter(iButtonApp* app) {
         key_data[5],
         key_data[6],
         key_data[7],
-        maxim_crc8(key_data, 7));
+        maxim_crc8(key_data, 7, MAXIM_CRC8_INIT));
 
     dialog_ex_set_header(dialog_ex, "CRC ERROR", 64, 10, AlignCenter, AlignCenter);
     dialog_ex_set_text(dialog_ex, app->get_text_store(), 64, 19, AlignCenter, AlignTop);
     dialog_ex_set_left_button_text(dialog_ex, "Retry");
     dialog_ex_set_right_button_text(dialog_ex, "More");
-    dialog_ex_set_result_callback(dialog_ex, callback);
+    dialog_ex_set_result_callback(dialog_ex, dialog_ex_callback);
     dialog_ex_set_context(dialog_ex, app);
 
     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewDialogEx);
@@ -65,14 +70,4 @@ void iButtonSceneReadCRCError::on_exit(iButtonApp* app) {
     dialog_ex_set_context(dialog_ex, NULL);
 
     app->notify_red_off();
-}
-
-void iButtonSceneReadCRCError::dialog_ex_callback(DialogExResult result, void* context) {
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-
-    event.type = iButtonEvent::Type::EventTypeDialogResult;
-    event.payload.dialog_result = result;
-
-    app->get_view_manager()->send_event(&event);
 }

+ 0 - 4
applications/ibutton/scene/ibutton_scene_read_crc_error.h

@@ -1,13 +1,9 @@
 #pragma once
 #include "ibutton_scene_generic.h"
-#include <gui/modules/dialog_ex.h>
 
 class iButtonSceneReadCRCError : public iButtonScene {
 public:
     void on_enter(iButtonApp* app) final;
     bool on_event(iButtonApp* app, iButtonEvent* event) final;
     void on_exit(iButtonApp* app) final;
-
-private:
-    void dialog_ex_callback(DialogExResult result, void* context);
 };

+ 14 - 19
applications/ibutton/scene/ibutton_scene_read_not_key_error.cpp

@@ -1,16 +1,21 @@
 #include "ibutton_scene_read_not_key_error.h"
 #include "../ibutton_app.h"
-#include "../ibutton_view_manager.h"
-#include "../ibutton_event.h"
-#include <callback-connector.h>
+#include <one_wire/maxim_crc.h>
+
+static void dialog_ex_callback(DialogExResult result, void* context) {
+    iButtonApp* app = static_cast<iButtonApp*>(context);
+    iButtonEvent event;
+
+    event.type = iButtonEvent::Type::EventTypeDialogResult;
+    event.payload.dialog_result = result;
+
+    app->get_view_manager()->send_event(&event);
+}
 
 void iButtonSceneReadNotKeyError::on_enter(iButtonApp* app) {
     iButtonAppViewManager* view_manager = app->get_view_manager();
     DialogEx* dialog_ex = view_manager->get_dialog_ex();
-    auto callback = cbc::obtain_connector(this, &iButtonSceneReadNotKeyError::dialog_ex_callback);
-
-    iButtonKey* key = app->get_key();
-    uint8_t* key_data = key->get_data();
+    const uint8_t* key_data = ibutton_key_get_data_p(app->get_key());
 
     app->set_text_store(
         "THIS IS NOT A KEY\n%02X %02X %02X %02X %02X %02X %02X %02X",
@@ -22,13 +27,13 @@ void iButtonSceneReadNotKeyError::on_enter(iButtonApp* app) {
         key_data[5],
         key_data[6],
         key_data[7],
-        maxim_crc8(key_data, 7));
+        maxim_crc8(key_data, 7, MAXIM_CRC8_INIT));
 
     dialog_ex_set_header(dialog_ex, "ERROR:", 64, 10, AlignCenter, AlignCenter);
     dialog_ex_set_text(dialog_ex, app->get_text_store(), 64, 19, AlignCenter, AlignTop);
     dialog_ex_set_left_button_text(dialog_ex, "Retry");
     dialog_ex_set_right_button_text(dialog_ex, "More");
-    dialog_ex_set_result_callback(dialog_ex, callback);
+    dialog_ex_set_result_callback(dialog_ex, dialog_ex_callback);
     dialog_ex_set_context(dialog_ex, app);
 
     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewDialogEx);
@@ -65,14 +70,4 @@ void iButtonSceneReadNotKeyError::on_exit(iButtonApp* app) {
     dialog_ex_set_context(dialog_ex, NULL);
 
     app->notify_red_off();
-}
-
-void iButtonSceneReadNotKeyError::dialog_ex_callback(DialogExResult result, void* context) {
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-
-    event.type = iButtonEvent::Type::EventTypeDialogResult;
-    event.payload.dialog_result = result;
-
-    app->get_view_manager()->send_event(&event);
 }

+ 0 - 4
applications/ibutton/scene/ibutton_scene_read_not_key_error.h

@@ -1,13 +1,9 @@
 #pragma once
 #include "ibutton_scene_generic.h"
-#include <gui/modules/dialog_ex.h>
 
 class iButtonSceneReadNotKeyError : public iButtonScene {
 public:
     void on_enter(iButtonApp* app) final;
     bool on_event(iButtonApp* app, iButtonEvent* event) final;
     void on_exit(iButtonApp* app) final;
-
-private:
-    void dialog_ex_callback(DialogExResult result, void* context);
 };

+ 17 - 22
applications/ibutton/scene/ibutton_scene_read_success.cpp

@@ -1,21 +1,26 @@
 #include "ibutton_scene_read_success.h"
 #include "../ibutton_app.h"
-#include "../ibutton_view_manager.h"
-#include "../ibutton_event.h"
 #include <dolphin/dolphin.h>
-#include <callback-connector.h>
+
+static void dialog_ex_callback(DialogExResult result, void* context) {
+    iButtonApp* app = static_cast<iButtonApp*>(context);
+    iButtonEvent event;
+
+    event.type = iButtonEvent::Type::EventTypeDialogResult;
+    event.payload.dialog_result = result;
+
+    app->get_view_manager()->send_event(&event);
+}
 
 void iButtonSceneReadSuccess::on_enter(iButtonApp* app) {
     iButtonAppViewManager* view_manager = app->get_view_manager();
     DialogEx* dialog_ex = view_manager->get_dialog_ex();
-    auto callback = cbc::obtain_connector(this, &iButtonSceneReadSuccess::dialog_ex_callback);
-    DOLPHIN_DEED(DolphinDeedIbuttonReadSuccess);
-
     iButtonKey* key = app->get_key();
-    uint8_t* key_data = key->get_data();
+    const uint8_t* key_data = ibutton_key_get_data_p(key);
+    DOLPHIN_DEED(DolphinDeedIbuttonReadSuccess);
 
-    switch(key->get_key_type()) {
-    case iButtonKeyType::KeyDallas:
+    switch(ibutton_key_get_type(key)) {
+    case iButtonKeyDS1990:
         app->set_text_store(
             "Dallas\n%02X %02X %02X %02X\n%02X %02X %02X %02X",
             key_data[0],
@@ -27,10 +32,10 @@ void iButtonSceneReadSuccess::on_enter(iButtonApp* app) {
             key_data[6],
             key_data[7]);
         break;
-    case iButtonKeyType::KeyCyfral:
+    case iButtonKeyCyfral:
         app->set_text_store("Cyfral\n%02X %02X", key_data[0], key_data[1]);
         break;
-    case iButtonKeyType::KeyMetakom:
+    case iButtonKeyMetakom:
         app->set_text_store(
             "Metakom\n%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]);
         break;
@@ -40,7 +45,7 @@ void iButtonSceneReadSuccess::on_enter(iButtonApp* app) {
     dialog_ex_set_left_button_text(dialog_ex, "Retry");
     dialog_ex_set_right_button_text(dialog_ex, "More");
     dialog_ex_set_icon(dialog_ex, 0, 1, &I_DolphinExcited_64x63);
-    dialog_ex_set_result_callback(dialog_ex, callback);
+    dialog_ex_set_result_callback(dialog_ex, dialog_ex_callback);
     dialog_ex_set_context(dialog_ex, app);
 
     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewDialogEx);
@@ -80,13 +85,3 @@ void iButtonSceneReadSuccess::on_exit(iButtonApp* app) {
 
     app->notify_green_off();
 }
-
-void iButtonSceneReadSuccess::dialog_ex_callback(DialogExResult result, void* context) {
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-
-    event.type = iButtonEvent::Type::EventTypeDialogResult;
-    event.payload.dialog_result = result;
-
-    app->get_view_manager()->send_event(&event);
-}

+ 0 - 4
applications/ibutton/scene/ibutton_scene_read_success.h

@@ -1,13 +1,9 @@
 #pragma once
 #include "ibutton_scene_generic.h"
-#include <gui/modules/dialog_ex.h>
 
 class iButtonSceneReadSuccess : public iButtonScene {
 public:
     void on_enter(iButtonApp* app) final;
     bool on_event(iButtonApp* app, iButtonEvent* event) final;
     void on_exit(iButtonApp* app) final;
-
-private:
-    void dialog_ex_callback(DialogExResult result, void* context);
 };

+ 16 - 20
applications/ibutton/scene/ibutton_scene_readed_key_menu.cpp

@@ -1,8 +1,5 @@
 #include "ibutton_scene_readed_key_menu.h"
 #include "../ibutton_app.h"
-#include "../ibutton_view_manager.h"
-#include "../ibutton_event.h"
-#include <callback-connector.h>
 
 typedef enum {
     SubmenuIndexWrite,
@@ -11,17 +8,26 @@ typedef enum {
     SubmenuIndexReadNewKey,
 } SubmenuIndex;
 
+static void submenu_callback(void* context, uint32_t index) {
+    iButtonApp* app = static_cast<iButtonApp*>(context);
+    iButtonEvent event;
+
+    event.type = iButtonEvent::Type::EventTypeMenuSelected;
+    event.payload.menu_index = index;
+
+    app->get_view_manager()->send_event(&event);
+}
+
 void iButtonSceneReadedKeyMenu::on_enter(iButtonApp* app) {
     iButtonAppViewManager* view_manager = app->get_view_manager();
     Submenu* submenu = view_manager->get_submenu();
-    auto callback = cbc::obtain_connector(this, &iButtonSceneReadedKeyMenu::submenu_callback);
 
-    if(app->get_key()->get_key_type() == iButtonKeyType::KeyDallas) {
-        submenu_add_item(submenu, "Write", SubmenuIndexWrite, callback, app);
+    if(ibutton_key_get_type(app->get_key()) == iButtonKeyDS1990) {
+        submenu_add_item(submenu, "Write", SubmenuIndexWrite, submenu_callback, app);
     }
-    submenu_add_item(submenu, "Name and save", SubmenuIndexNameAndSave, callback, app);
-    submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, callback, app);
-    submenu_add_item(submenu, "Read new key", SubmenuIndexReadNewKey, callback, app);
+    submenu_add_item(submenu, "Name and save", SubmenuIndexNameAndSave, submenu_callback, app);
+    submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, submenu_callback, app);
+    submenu_add_item(submenu, "Read new key", SubmenuIndexReadNewKey, submenu_callback, app);
     submenu_set_selected_item(submenu, submenu_item_selected);
 
     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu);
@@ -60,14 +66,4 @@ void iButtonSceneReadedKeyMenu::on_exit(iButtonApp* app) {
     Submenu* submenu = view->get_submenu();
 
     submenu_reset(submenu);
-}
-
-void iButtonSceneReadedKeyMenu::submenu_callback(void* context, uint32_t index) {
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-
-    event.type = iButtonEvent::Type::EventTypeMenuSelected;
-    event.payload.menu_index = index;
-
-    app->get_view_manager()->send_event(&event);
-}
+}

+ 0 - 1
applications/ibutton/scene/ibutton_scene_readed_key_menu.h

@@ -8,6 +8,5 @@ public:
     void on_exit(iButtonApp* app) final;
 
 private:
-    void submenu_callback(void* context, uint32_t index);
     uint32_t submenu_item_selected = 0;
 };

+ 16 - 17
applications/ibutton/scene/ibutton_scene_save_name.cpp

@@ -1,18 +1,21 @@
 #include "ibutton_scene_save_name.h"
 #include "../ibutton_app.h"
-#include "../ibutton_view_manager.h"
-#include "../ibutton_event.h"
-#include "../ibutton_key.h"
-#include <callback-connector.h>
 #include <lib/toolbox/random_name.h>
 
+static void text_input_callback(void* context) {
+    iButtonApp* app = static_cast<iButtonApp*>(context);
+    iButtonEvent event;
+
+    event.type = iButtonEvent::Type::EventTypeTextEditResult;
+
+    app->get_view_manager()->send_event(&event);
+}
+
 void iButtonSceneSaveName::on_enter(iButtonApp* app) {
     iButtonAppViewManager* view_manager = app->get_view_manager();
     TextInput* text_input = view_manager->get_text_input();
-    auto callback = cbc::obtain_connector(this, &iButtonSceneSaveName::text_input_callback);
 
-    iButtonKey* key = app->get_key();
-    const char* key_name = key->get_name();
+    const char* key_name = ibutton_key_get_name_p(app->get_key());
     bool key_name_empty = !strcmp(key_name, "");
 
     if(key_name_empty) {
@@ -23,7 +26,12 @@ void iButtonSceneSaveName::on_enter(iButtonApp* app) {
 
     text_input_set_header_text(text_input, "Name the key");
     text_input_set_result_callback(
-        text_input, callback, app, app->get_text_store(), IBUTTON_KEY_NAME_SIZE, key_name_empty);
+        text_input,
+        text_input_callback,
+        app,
+        app->get_text_store(),
+        IBUTTON_KEY_NAME_SIZE,
+        key_name_empty);
 
     ValidatorIsFile* validator_is_file =
         validator_is_file_alloc_init(app->app_folder, app->app_extension);
@@ -59,12 +67,3 @@ void iButtonSceneSaveName::on_exit(iButtonApp* app) {
 
     text_input_reset(text_input);
 }
-
-void iButtonSceneSaveName::text_input_callback(void* context) {
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-
-    event.type = iButtonEvent::Type::EventTypeTextEditResult;
-
-    app->get_view_manager()->send_event(&event);
-}

+ 0 - 3
applications/ibutton/scene/ibutton_scene_save_name.h

@@ -6,7 +6,4 @@ public:
     void on_enter(iButtonApp* app) final;
     bool on_event(iButtonApp* app, iButtonEvent* event) final;
     void on_exit(iButtonApp* app) final;
-
-private:
-    void text_input_callback(void* context);
 };

+ 9 - 14
applications/ibutton/scene/ibutton_scene_save_success.cpp

@@ -1,21 +1,23 @@
 #include "ibutton_scene_save_success.h"
 #include "../ibutton_app.h"
-#include "../ibutton_view_manager.h"
-#include "../ibutton_event.h"
-#include "../ibutton_key.h"
 #include <dolphin/dolphin.h>
-#include <callback-connector.h>
+
+static void popup_callback(void* context) {
+    iButtonApp* app = static_cast<iButtonApp*>(context);
+    iButtonEvent event;
+    event.type = iButtonEvent::Type::EventTypeBack;
+    app->get_view_manager()->send_event(&event);
+}
 
 void iButtonSceneSaveSuccess::on_enter(iButtonApp* app) {
     iButtonAppViewManager* view_manager = app->get_view_manager();
     Popup* popup = view_manager->get_popup();
-    auto callback = cbc::obtain_connector(this, &iButtonSceneSaveSuccess::popup_callback);
     DOLPHIN_DEED(DolphinDeedIbuttonSave);
 
     popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
     popup_set_text(popup, "Saved!", 13, 22, AlignLeft, AlignBottom);
 
-    popup_set_callback(popup, callback);
+    popup_set_callback(popup, popup_callback);
     popup_set_context(popup, app);
     popup_set_timeout(popup, 1500);
     popup_enable_timeout(popup);
@@ -46,11 +48,4 @@ void iButtonSceneSaveSuccess::on_exit(iButtonApp* app) {
     popup_disable_timeout(popup);
     popup_set_context(popup, NULL);
     popup_set_callback(popup, NULL);
-}
-
-void iButtonSceneSaveSuccess::popup_callback(void* context) {
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-    event.type = iButtonEvent::Type::EventTypeBack;
-    app->get_view_manager()->send_event(&event);
-}
+}

+ 0 - 3
applications/ibutton/scene/ibutton_scene_save_success.h

@@ -6,7 +6,4 @@ public:
     void on_enter(iButtonApp* app) final;
     bool on_event(iButtonApp* app, iButtonEvent* event) final;
     void on_exit(iButtonApp* app) final;
-
-private:
-    void popup_callback(void* context);
 };

+ 16 - 19
applications/ibutton/scene/ibutton_scene_saved_key_menu.cpp

@@ -1,7 +1,5 @@
 #include "ibutton_scene_saved_key_menu.h"
 #include "../ibutton_app.h"
-#include "../ibutton_view_manager.h"
-#include "../ibutton_event.h"
 #include <callback-connector.h>
 
 typedef enum {
@@ -12,18 +10,27 @@ typedef enum {
     SubmenuIndexInfo,
 } SubmenuIndex;
 
+static void submenu_callback(void* context, uint32_t index) {
+    iButtonApp* app = static_cast<iButtonApp*>(context);
+    iButtonEvent event;
+
+    event.type = iButtonEvent::Type::EventTypeMenuSelected;
+    event.payload.menu_index = index;
+
+    app->get_view_manager()->send_event(&event);
+}
+
 void iButtonSceneSavedKeyMenu::on_enter(iButtonApp* app) {
     iButtonAppViewManager* view_manager = app->get_view_manager();
     Submenu* submenu = view_manager->get_submenu();
-    auto callback = cbc::obtain_connector(this, &iButtonSceneSavedKeyMenu::submenu_callback);
 
-    submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, callback, app);
-    if(app->get_key()->get_key_type() == iButtonKeyType::KeyDallas) {
-        submenu_add_item(submenu, "Write", SubmenuIndexWrite, callback, app);
+    submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, submenu_callback, app);
+    if(ibutton_key_get_type(app->get_key()) == iButtonKeyDS1990) {
+        submenu_add_item(submenu, "Write", SubmenuIndexWrite, submenu_callback, app);
     }
-    submenu_add_item(submenu, "Edit", SubmenuIndexEdit, callback, app);
-    submenu_add_item(submenu, "Delete", SubmenuIndexDelete, callback, app);
-    submenu_add_item(submenu, "Info", SubmenuIndexInfo, callback, app);
+    submenu_add_item(submenu, "Edit", SubmenuIndexEdit, submenu_callback, app);
+    submenu_add_item(submenu, "Delete", SubmenuIndexDelete, submenu_callback, app);
+    submenu_add_item(submenu, "Info", SubmenuIndexInfo, submenu_callback, app);
     submenu_set_selected_item(submenu, submenu_item_selected);
 
     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu);
@@ -63,13 +70,3 @@ void iButtonSceneSavedKeyMenu::on_exit(iButtonApp* app) {
 
     submenu_reset(submenu);
 }
-
-void iButtonSceneSavedKeyMenu::submenu_callback(void* context, uint32_t index) {
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-
-    event.type = iButtonEvent::Type::EventTypeMenuSelected;
-    event.payload.menu_index = index;
-
-    app->get_view_manager()->send_event(&event);
-}

+ 0 - 1
applications/ibutton/scene/ibutton_scene_saved_key_menu.h

@@ -8,6 +8,5 @@ public:
     void on_exit(iButtonApp* app) final;
 
 private:
-    void submenu_callback(void* context, uint32_t index);
     uint32_t submenu_item_selected = 0;
 };

+ 0 - 2
applications/ibutton/scene/ibutton_scene_select_key.cpp

@@ -1,7 +1,5 @@
 #include "ibutton_scene_select_key.h"
 #include "../ibutton_app.h"
-#include "../ibutton_event.h"
-#include "../ibutton_key.h"
 
 void iButtonSceneSelectKey::on_enter(iButtonApp* app) {
     // Process file_select return

+ 13 - 17
applications/ibutton/scene/ibutton_scene_start.cpp

@@ -1,8 +1,5 @@
 #include "ibutton_scene_start.h"
 #include "../ibutton_app.h"
-#include "../ibutton_view_manager.h"
-#include "../ibutton_event.h"
-#include <callback-connector.h>
 
 typedef enum {
     SubmenuIndexRead,
@@ -10,14 +7,23 @@ typedef enum {
     SubmenuIndexAdd,
 } SubmenuIndex;
 
+static void submenu_callback(void* context, uint32_t index) {
+    iButtonApp* app = static_cast<iButtonApp*>(context);
+    iButtonEvent event;
+
+    event.type = iButtonEvent::Type::EventTypeMenuSelected;
+    event.payload.menu_index = index;
+
+    app->get_view_manager()->send_event(&event);
+}
+
 void iButtonSceneStart::on_enter(iButtonApp* app) {
     iButtonAppViewManager* view_manager = app->get_view_manager();
     Submenu* submenu = view_manager->get_submenu();
-    auto callback = cbc::obtain_connector(this, &iButtonSceneStart::submenu_callback);
 
-    submenu_add_item(submenu, "Read", SubmenuIndexRead, callback, app);
-    submenu_add_item(submenu, "Saved", SubmenuIndexSaved, callback, app);
-    submenu_add_item(submenu, "Add manually", SubmenuIndexAdd, callback, app);
+    submenu_add_item(submenu, "Read", SubmenuIndexRead, submenu_callback, app);
+    submenu_add_item(submenu, "Saved", SubmenuIndexSaved, submenu_callback, app);
+    submenu_add_item(submenu, "Add manually", SubmenuIndexAdd, submenu_callback, app);
     submenu_set_selected_item(submenu, submenu_item_selected);
 
     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu);
@@ -51,13 +57,3 @@ void iButtonSceneStart::on_exit(iButtonApp* app) {
 
     submenu_reset(submenu);
 }
-
-void iButtonSceneStart::submenu_callback(void* context, uint32_t index) {
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-
-    event.type = iButtonEvent::Type::EventTypeMenuSelected;
-    event.payload.menu_index = index;
-
-    app->get_view_manager()->send_event(&event);
-}

+ 0 - 1
applications/ibutton/scene/ibutton_scene_start.h

@@ -8,6 +8,5 @@ public:
     void on_exit(iButtonApp* app) final;
 
 private:
-    void submenu_callback(void* context, uint32_t index);
     uint32_t submenu_item_selected = 0;
 };

+ 34 - 22
applications/ibutton/scene/ibutton_scene_write.cpp

@@ -1,15 +1,22 @@
 #include "ibutton_scene_write.h"
 #include "../ibutton_app.h"
-#include "../ibutton_view_manager.h"
-#include "../ibutton_event.h"
-#include "../ibutton_key.h"
+
+static void ibutton_worker_write_cb(void* context, iButtonWorkerWriteResult result) {
+    iButtonApp* app = static_cast<iButtonApp*>(context);
+    iButtonEvent event;
+    event.type = iButtonEvent::Type::EventTypeWorkerWrite;
+    event.payload.worker_write_result = result;
+
+    app->get_view_manager()->send_event(&event);
+}
 
 void iButtonSceneWrite::on_enter(iButtonApp* app) {
     iButtonAppViewManager* view_manager = app->get_view_manager();
     Popup* popup = view_manager->get_popup();
     iButtonKey* key = app->get_key();
-    uint8_t* key_data = key->get_data();
-    const char* key_name = key->get_name();
+    iButtonWorker* worker = app->get_key_worker();
+    const uint8_t* key_data = ibutton_key_get_data_p(key);
+    const char* key_name = ibutton_key_get_name_p(key);
     uint8_t line_count = 2;
 
     // check that stored key has name
@@ -18,8 +25,8 @@ void iButtonSceneWrite::on_enter(iButtonApp* app) {
         line_count = 2;
     } else {
         // if not, show key data
-        switch(key->get_key_type()) {
-        case iButtonKeyType::KeyDallas:
+        switch(ibutton_key_get_type(key)) {
+        case iButtonKeyDS1990:
             app->set_text_store(
                 "writing\n%02X %02X %02X %02X\n%02X %02X %02X %02X",
                 key_data[0],
@@ -32,11 +39,11 @@ void iButtonSceneWrite::on_enter(iButtonApp* app) {
                 key_data[7]);
             line_count = 3;
             break;
-        case iButtonKeyType::KeyCyfral:
+        case iButtonKeyCyfral:
             app->set_text_store("writing\n%02X %02X", key_data[0], key_data[1]);
             line_count = 2;
             break;
-        case iButtonKeyType::KeyMetakom:
+        case iButtonKeyMetakom:
             app->set_text_store(
                 "writing\n%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]);
             line_count = 2;
@@ -60,28 +67,35 @@ void iButtonSceneWrite::on_enter(iButtonApp* app) {
 
     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewPopup);
 
-    app->get_key_worker()->start_write();
+    blink_yellow = false;
+    ibutton_worker_write_set_callback(worker, ibutton_worker_write_cb, app);
+    ibutton_worker_write_start(worker, key);
 }
 
 bool iButtonSceneWrite::on_event(iButtonApp* app, iButtonEvent* event) {
     bool consumed = false;
 
-    if(event->type == iButtonEvent::Type::EventTypeTick) {
+    if(event->type == iButtonEvent::Type::EventTypeWorkerWrite) {
         consumed = true;
-        KeyWriter::Error result = app->get_key_worker()->write(app->get_key());
 
-        switch(result) {
-        case KeyWriter::Error::SAME_KEY:
-        case KeyWriter::Error::OK:
+        switch(event->payload.worker_write_result) {
+        case iButtonWorkerWriteOK:
+        case iButtonWorkerWriteSameKey:
             app->switch_to_next_scene(iButtonApp::Scene::SceneWriteSuccess);
             break;
-        case KeyWriter::Error::NO_DETECT:
-            app->notify_red_blink();
+        case iButtonWorkerWriteNoDetect:
+            blink_yellow = false;
             break;
-        case KeyWriter::Error::CANNOT_WRITE:
-            app->notify_yellow_blink();
+        case iButtonWorkerWriteCannotWrite:
+            blink_yellow = true;
             break;
         }
+    } else if(event->type == iButtonEvent::Type::EventTypeTick) {
+        if(blink_yellow) {
+            app->notify_yellow_blink();
+        } else {
+            app->notify_red_blink();
+        }
     }
 
     return consumed;
@@ -89,10 +103,8 @@ bool iButtonSceneWrite::on_event(iButtonApp* app, iButtonEvent* event) {
 
 void iButtonSceneWrite::on_exit(iButtonApp* app) {
     Popup* popup = app->get_view_manager()->get_popup();
-
+    ibutton_worker_stop(app->get_key_worker());
     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
     popup_set_icon(popup, 0, 0, NULL);
-
-    app->get_key_worker()->stop_write();
 }

+ 1 - 0
applications/ibutton/scene/ibutton_scene_write.h

@@ -8,4 +8,5 @@ public:
     void on_exit(iButtonApp* app) final;
 
 private:
+    bool blink_yellow;
 };

+ 9 - 14
applications/ibutton/scene/ibutton_scene_write_success.cpp

@@ -1,19 +1,22 @@
 #include "ibutton_scene_write_success.h"
 #include "../ibutton_app.h"
-#include "../ibutton_view_manager.h"
-#include "../ibutton_event.h"
-#include "../ibutton_key.h"
-#include <callback-connector.h>
+
+static void popup_callback(void* context) {
+    iButtonApp* app = static_cast<iButtonApp*>(context);
+    iButtonEvent event;
+    event.type = iButtonEvent::Type::EventTypeBack;
+    app->get_view_manager()->send_event(&event);
+    app->notify_green_off();
+}
 
 void iButtonSceneWriteSuccess::on_enter(iButtonApp* app) {
     iButtonAppViewManager* view_manager = app->get_view_manager();
     Popup* popup = view_manager->get_popup();
-    auto callback = cbc::obtain_connector(this, &iButtonSceneWriteSuccess::popup_callback);
 
     popup_set_icon(popup, 0, 12, &I_iButtonDolphinVerySuccess_108x52);
     popup_set_text(popup, "Successfully written!", 40, 12, AlignLeft, AlignBottom);
 
-    popup_set_callback(popup, callback);
+    popup_set_callback(popup, popup_callback);
     popup_set_context(popup, app);
     popup_set_timeout(popup, 1500);
     popup_enable_timeout(popup);
@@ -45,12 +48,4 @@ void iButtonSceneWriteSuccess::on_exit(iButtonApp* app) {
     popup_disable_timeout(popup);
     popup_set_context(popup, NULL);
     popup_set_callback(popup, NULL);
-}
-
-void iButtonSceneWriteSuccess::popup_callback(void* context) {
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-    event.type = iButtonEvent::Type::EventTypeBack;
-    app->get_view_manager()->send_event(&event);
-    app->notify_green_off();
 }

+ 0 - 3
applications/ibutton/scene/ibutton_scene_write_success.h

@@ -6,7 +6,4 @@ public:
     void on_enter(iButtonApp* app) final;
     bool on_event(iButtonApp* app, iButtonEvent* event) final;
     void on_exit(iButtonApp* app) final;
-
-private:
-    void popup_callback(void* context);
 };

+ 3 - 4
firmware/targets/f7/furi_hal/furi_hal_delay.c

@@ -4,20 +4,19 @@
 #include <cmsis_os2.h>
 
 #define TAG "FuriHalDelay"
-
-static uint32_t clk_per_microsecond;
+uint32_t instructions_per_us;
 
 void furi_hal_delay_init(void) {
     CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
     DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
     DWT->CYCCNT = 0U;
-    clk_per_microsecond = SystemCoreClock / 1000000.0f;
+    instructions_per_us = SystemCoreClock / 1000000.0f;
     FURI_LOG_I(TAG, "Init OK");
 }
 
 void delay_us(float microseconds) {
     uint32_t start = DWT->CYCCNT;
-    uint32_t time_ticks = microseconds * clk_per_microsecond;
+    uint32_t time_ticks = microseconds * instructions_per_us;
     while((DWT->CYCCNT - start) < time_ticks) {
     };
 }

+ 25 - 1
firmware/targets/f7/furi_hal/furi_hal_ibutton.c

@@ -3,6 +3,7 @@
 #include <furi_hal_resources.h>
 
 #include <stm32wbxx_ll_tim.h>
+#include <stm32wbxx_ll_exti.h>
 
 #include <furi.h>
 
@@ -91,16 +92,39 @@ void furi_hal_ibutton_emulate_stop() {
     }
 }
 
-void furi_hal_ibutton_start() {
+void furi_hal_ibutton_start_drive() {
     furi_hal_ibutton_pin_high();
     hal_gpio_init(&ibutton_gpio, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow);
 }
 
+void furi_hal_ibutton_start_drive_in_isr() {
+    hal_gpio_init(&ibutton_gpio, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow);
+    LL_EXTI_ClearFlag_0_31(ibutton_gpio.pin);
+}
+
+void furi_hal_ibutton_start_interrupt() {
+    furi_hal_ibutton_pin_high();
+    hal_gpio_init(&ibutton_gpio, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedLow);
+}
+
+void furi_hal_ibutton_start_interrupt_in_isr() {
+    hal_gpio_init(&ibutton_gpio, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedLow);
+    LL_EXTI_ClearFlag_0_31(ibutton_gpio.pin);
+}
+
 void furi_hal_ibutton_stop() {
     furi_hal_ibutton_pin_high();
     hal_gpio_init(&ibutton_gpio, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
 }
 
+void furi_hal_ibutton_add_interrupt(GpioExtiCallback cb, void* context) {
+    hal_gpio_add_int_callback(&ibutton_gpio, cb, context);
+}
+
+void furi_hal_ibutton_remove_interrupt() {
+    hal_gpio_remove_int_callback(&ibutton_gpio);
+}
+
 void furi_hal_ibutton_pin_low() {
     hal_gpio_write(&ibutton_gpio, false);
 }

+ 9 - 2
firmware/targets/f7/furi_hal/furi_hal_rfid.c

@@ -23,6 +23,13 @@ typedef struct {
 
 FuriHalRfid* furi_hal_rfid = NULL;
 
+#define LFRFID_LL_READ_TIM TIM1
+#define LFRFID_LL_READ_CONFIG_CHANNEL LL_TIM_CHANNEL_CH1
+#define LFRFID_LL_READ_CHANNEL LL_TIM_CHANNEL_CH1N
+
+#define LFRFID_LL_EMULATE_TIM TIM2
+#define LFRFID_LL_EMULATE_CHANNEL LL_TIM_CHANNEL_CH3
+
 void furi_hal_rfid_init() {
     furi_assert(furi_hal_rfid == NULL);
     furi_hal_rfid = malloc(sizeof(FuriHalRfid));
@@ -72,7 +79,7 @@ void furi_hal_rfid_pins_reset() {
 
 void furi_hal_rfid_pins_emulate() {
     // ibutton low
-    furi_hal_ibutton_start();
+    furi_hal_ibutton_start_drive();
     furi_hal_ibutton_pin_low();
 
     // pull pin to timer out
@@ -89,7 +96,7 @@ void furi_hal_rfid_pins_emulate() {
 
 void furi_hal_rfid_pins_read() {
     // ibutton low
-    furi_hal_ibutton_start();
+    furi_hal_ibutton_start_drive();
     furi_hal_ibutton_pin_low();
 
     // dont pull rfid antenna

+ 2 - 0
firmware/targets/furi_hal_include/furi_hal_delay.h

@@ -11,6 +11,8 @@
 extern "C" {
 #endif
 
+extern uint32_t instructions_per_us;
+
 /** Init DWT
  */
 void furi_hal_delay_init(void);

+ 48 - 1
firmware/targets/furi_hal_include/furi_hal_ibutton.h

@@ -7,6 +7,7 @@
 
 #include <stdbool.h>
 #include <stdint.h>
+#include "furi_hal_gpio.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -26,14 +27,60 @@ void furi_hal_ibutton_emulate_set_next(uint32_t period);
 
 void furi_hal_ibutton_emulate_stop();
 
-void furi_hal_ibutton_start();
+/**
+ * Sets the pin to normal mode (open collector), and sets it to float
+ */
+void furi_hal_ibutton_start_drive();
 
+/**
+ * Sets the pin to normal mode (open collector), and clears pin EXTI interrupt.
+ * Used in EXTI interrupt context.
+ */
+void furi_hal_ibutton_start_drive_in_isr();
+
+/**
+ * Sets the pin to interrupt mode (EXTI interrupt on rise or fall), and sets it to float
+ */
+void furi_hal_ibutton_start_interrupt();
+
+/**
+ * Sets the pin to interrupt mode (EXTI interrupt on rise or fall), and clears pin EXTI interrupt.
+ * Used in EXTI interrupt context.
+ */
+void furi_hal_ibutton_start_interrupt_in_isr();
+
+/**
+ * Sets the pin to analog mode, and sets it to float
+ */
 void furi_hal_ibutton_stop();
 
+/**
+ * Attach interrupt callback to iButton pin
+ * @param cb callback
+ * @param context context
+ */
+void furi_hal_ibutton_add_interrupt(GpioExtiCallback cb, void* context);
+
+/**
+ * Remove interrupt callback from iButton pin
+ */
+void furi_hal_ibutton_remove_interrupt();
+
+/**
+ * Sets the pin to low
+ */
 void furi_hal_ibutton_pin_low();
 
+/**
+ * Sets the pin to high (float in iButton pin modes)
+ */
 void furi_hal_ibutton_pin_high();
 
+/**
+ * Get pin level
+ * @return true if level is high
+ * @return false if level is low
+ */
 bool furi_hal_ibutton_pin_get_level();
 
 #ifdef __cplusplus

+ 5 - 10
lib/lib.mk

@@ -65,16 +65,6 @@ CFLAGS			+= -I$(LIB_DIR)/app_scene_template
 CFLAGS			+= -I$(LIB_DIR)/fnv1a-hash
 C_SOURCES		+= $(LIB_DIR)/fnv1a-hash/fnv1a-hash.c
 
-# onewire library
-ONEWIRE_DIR		= $(LIB_DIR)/onewire
-CFLAGS			+= -I$(ONEWIRE_DIR)
-CPP_SOURCES		+= $(wildcard $(ONEWIRE_DIR)/*.cpp)
-
-# cyfral library
-CYFRAL_DIR		= $(LIB_DIR)/cyfral
-CFLAGS			+= -I$(CYFRAL_DIR)
-CPP_SOURCES		+= $(wildcard $(CYFRAL_DIR)/*.cpp)
-
 # common apps api
 CFLAGS			+= -I$(LIB_DIR)/common-api
 
@@ -128,3 +118,8 @@ C_SOURCES		+= $(wildcard $(LIB_DIR)/flipper_format/*.c)
 # Micro-ECC
 CFLAGS			+= -I$(LIB_DIR)/micro-ecc
 C_SOURCES		+= $(wildcard $(LIB_DIR)/micro-ecc/*.c)
+
+# iButton and OneWire
+C_SOURCES		+= $(wildcard $(LIB_DIR)/one_wire/*.c)
+C_SOURCES		+= $(wildcard $(LIB_DIR)/one_wire/*/*.c)
+C_SOURCES		+= $(wildcard $(LIB_DIR)/one_wire/*/*/*.c)

+ 126 - 0
lib/one_wire/ibutton/encoder/encoder_cyfral.c

@@ -0,0 +1,126 @@
+#include "encoder_cyfral.h"
+#include <furi_hal.h>
+
+#define CYFRAL_DATA_SIZE sizeof(uint16_t)
+#define CYFRAL_PERIOD (125 * instructions_per_us)
+#define CYFRAL_0_LOW (CYFRAL_PERIOD * 0.66f)
+#define CYFRAL_0_HI (CYFRAL_PERIOD * 0.33f)
+#define CYFRAL_1_LOW (CYFRAL_PERIOD * 0.33f)
+#define CYFRAL_1_HI (CYFRAL_PERIOD * 0.66f)
+
+#define CYFRAL_SET_DATA(level, len) \
+    *polarity = level;              \
+    *length = len;
+
+struct EncoderCyfral {
+    uint32_t data;
+    uint32_t index;
+};
+
+EncoderCyfral* encoder_cyfral_alloc() {
+    EncoderCyfral* cyfral = malloc(sizeof(EncoderCyfral));
+    encoder_cyfral_reset(cyfral);
+    return cyfral;
+}
+
+void encoder_cyfral_free(EncoderCyfral* cyfral) {
+    free(cyfral);
+}
+
+void encoder_cyfral_reset(EncoderCyfral* cyfral) {
+    cyfral->data = 0;
+    cyfral->index = 0;
+}
+
+uint32_t cyfral_encoder_encode(const uint16_t data) {
+    uint32_t value = 0;
+    for(int8_t i = 0; i <= 7; i++) {
+        switch((data >> (i * 2)) & 0b00000011) {
+        case 0b11:
+            value = value << 4;
+            value += 0b00000111;
+            break;
+        case 0b10:
+            value = value << 4;
+            value += 0b00001011;
+            break;
+        case 0b01:
+            value = value << 4;
+            value += 0b00001101;
+            break;
+        case 0b00:
+            value = value << 4;
+            value += 0b00001110;
+            break;
+        default:
+            break;
+        }
+    }
+
+    return value;
+}
+
+void encoder_cyfral_set_data(EncoderCyfral* cyfral, const uint8_t* data, size_t data_size) {
+    furi_assert(cyfral);
+    furi_check(data_size >= CYFRAL_DATA_SIZE);
+    uint16_t intermediate;
+    memcpy(&intermediate, data, CYFRAL_DATA_SIZE);
+    cyfral->data = cyfral_encoder_encode(intermediate);
+}
+
+void encoder_cyfral_get_pulse(EncoderCyfral* cyfral, bool* polarity, uint32_t* length) {
+    if(cyfral->index < 8) {
+        // start word (0b0001)
+        switch(cyfral->index) {
+        case 0:
+            CYFRAL_SET_DATA(false, CYFRAL_0_LOW);
+            break;
+        case 1:
+            CYFRAL_SET_DATA(true, CYFRAL_0_HI);
+            break;
+        case 2:
+            CYFRAL_SET_DATA(false, CYFRAL_0_LOW);
+            break;
+        case 3:
+            CYFRAL_SET_DATA(true, CYFRAL_0_HI);
+            break;
+        case 4:
+            CYFRAL_SET_DATA(false, CYFRAL_0_LOW);
+            break;
+        case 5:
+            CYFRAL_SET_DATA(true, CYFRAL_0_HI);
+            break;
+        case 6:
+            CYFRAL_SET_DATA(false, CYFRAL_1_LOW);
+            break;
+        case 7:
+            CYFRAL_SET_DATA(true, CYFRAL_1_HI);
+            break;
+        }
+    } else {
+        // data
+        uint8_t data_start_index = cyfral->index - 8;
+        bool clock_polarity = (data_start_index) % 2;
+        uint8_t bit_index = (data_start_index) / 2;
+        bool bit_value = ((cyfral->data >> bit_index) & 1);
+
+        if(!clock_polarity) {
+            if(bit_value) {
+                CYFRAL_SET_DATA(false, CYFRAL_1_LOW);
+            } else {
+                CYFRAL_SET_DATA(false, CYFRAL_0_LOW);
+            }
+        } else {
+            if(bit_value) {
+                CYFRAL_SET_DATA(true, CYFRAL_1_HI);
+            } else {
+                CYFRAL_SET_DATA(true, CYFRAL_0_HI);
+            }
+        }
+    }
+
+    cyfral->index++;
+    if(cyfral->index >= (9 * 4 * 2)) {
+        cyfral->index = 0;
+    }
+}

+ 54 - 0
lib/one_wire/ibutton/encoder/encoder_cyfral.h

@@ -0,0 +1,54 @@
+/**
+ * @file encoder_cyfral.h
+ * 
+ * Cyfral pulse format encoder
+ */
+
+#pragma once
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct EncoderCyfral EncoderCyfral;
+
+/**
+ * Allocate Cyfral encoder
+ * @return EncoderCyfral* 
+ */
+EncoderCyfral* encoder_cyfral_alloc();
+
+/**
+ * Deallocate Cyfral encoder
+ * @param cyfral 
+ */
+void encoder_cyfral_free(EncoderCyfral* cyfral);
+
+/**
+ * Reset Cyfral encoder
+ * @param cyfral 
+ */
+void encoder_cyfral_reset(EncoderCyfral* cyfral);
+
+/**
+ * Set data to be encoded to Cyfral pulse format, 2 bytes
+ * @param cyfral 
+ * @param data 
+ * @param data_size
+ */
+void encoder_cyfral_set_data(EncoderCyfral* cyfral, const uint8_t* data, size_t data_size);
+
+/**
+ * Pop pulse from Cyfral encoder
+ * @param cyfral 
+ * @param polarity 
+ * @param length 
+ */
+void encoder_cyfral_get_pulse(EncoderCyfral* cyfral, bool* polarity, uint32_t* length);
+
+#ifdef __cplusplus
+}
+#endif

+ 93 - 0
lib/one_wire/ibutton/encoder/encoder_metakom.c

@@ -0,0 +1,93 @@
+#include "encoder_metakom.h"
+#include <furi_hal.h>
+
+#define METAKOM_DATA_SIZE sizeof(uint32_t)
+#define METAKOM_PERIOD (125 * instructions_per_us)
+#define METAKOM_0_LOW (METAKOM_PERIOD * 0.33f)
+#define METAKOM_0_HI (METAKOM_PERIOD * 0.66f)
+#define METAKOM_1_LOW (METAKOM_PERIOD * 0.66f)
+#define METAKOM_1_HI (METAKOM_PERIOD * 0.33f)
+
+#define METAKOM_SET_DATA(level, len) \
+    *polarity = !level;              \
+    *length = len;
+
+struct EncoderMetakom {
+    uint32_t data;
+    uint32_t index;
+};
+
+EncoderMetakom* encoder_metakom_alloc() {
+    EncoderMetakom* metakom = malloc(sizeof(EncoderMetakom));
+    encoder_metakom_reset(metakom);
+    return metakom;
+}
+
+void encoder_metakom_free(EncoderMetakom* metakom) {
+    free(metakom);
+}
+
+void encoder_metakom_reset(EncoderMetakom* metakom) {
+    metakom->data = 0;
+    metakom->index = 0;
+}
+
+void encoder_metakom_set_data(EncoderMetakom* metakom, const uint8_t* data, size_t data_size) {
+    furi_assert(metakom);
+    furi_check(data_size >= METAKOM_DATA_SIZE);
+    memcpy(&metakom->data, data, METAKOM_DATA_SIZE);
+}
+
+void encoder_metakom_get_pulse(EncoderMetakom* metakom, bool* polarity, uint32_t* length) {
+    if(metakom->index == 0) {
+        // sync bit
+        METAKOM_SET_DATA(true, METAKOM_PERIOD);
+    } else if(metakom->index >= 1 && metakom->index <= 6) {
+        // start word (0b010)
+        switch(metakom->index) {
+        case 1:
+            METAKOM_SET_DATA(false, METAKOM_0_LOW);
+            break;
+        case 2:
+            METAKOM_SET_DATA(true, METAKOM_0_HI);
+            break;
+        case 3:
+            METAKOM_SET_DATA(false, METAKOM_1_LOW);
+            break;
+        case 4:
+            METAKOM_SET_DATA(true, METAKOM_1_HI);
+            break;
+        case 5:
+            METAKOM_SET_DATA(false, METAKOM_0_LOW);
+            break;
+        case 6:
+            METAKOM_SET_DATA(true, METAKOM_0_HI);
+            break;
+        }
+    } else {
+        // data
+        uint8_t data_start_index = metakom->index - 7;
+        bool clock_polarity = (data_start_index) % 2;
+        uint8_t bit_index = (data_start_index) / 2;
+        bool bit_value = (metakom->data >> (32 - 1 - bit_index)) & 1;
+
+        if(!clock_polarity) {
+            if(bit_value) {
+                METAKOM_SET_DATA(false, METAKOM_1_LOW);
+            } else {
+                METAKOM_SET_DATA(false, METAKOM_0_LOW);
+            }
+        } else {
+            if(bit_value) {
+                METAKOM_SET_DATA(true, METAKOM_1_HI);
+            } else {
+                METAKOM_SET_DATA(true, METAKOM_0_HI);
+            }
+        }
+    }
+
+    metakom->index++;
+    if(metakom->index >= (1 + 3 * 2 + 32 * 2)) {
+        metakom->index = 0;
+    }
+}

+ 54 - 0
lib/one_wire/ibutton/encoder/encoder_metakom.h

@@ -0,0 +1,54 @@
+/**
+ * @file encoder_metakom.h
+ * 
+ * Metakom pulse format encoder
+ */
+
+#pragma once
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct EncoderMetakom EncoderMetakom;
+
+/**
+ * Allocate Metakom encoder
+ * @return EncoderMetakom* 
+ */
+EncoderMetakom* encoder_metakom_alloc();
+
+/**
+ * Deallocate Metakom encoder
+ * @param metakom 
+ */
+void encoder_metakom_free(EncoderMetakom* metakom);
+
+/**
+ * Reset Metakom encoder
+ * @param metakom 
+ */
+void encoder_metakom_reset(EncoderMetakom* metakom);
+
+/**
+ * Set data to be encoded to Metakom pulse format, 4 bytes
+ * @param metakom 
+ * @param data 
+ * @param data_size 
+ */
+void encoder_metakom_set_data(EncoderMetakom* metakom, const uint8_t* data, size_t data_size);
+
+/**
+ * Pop pulse from Metakom encoder
+ * @param cyfral 
+ * @param polarity 
+ * @param length 
+ */
+void encoder_metakom_get_pulse(EncoderMetakom* metakom, bool* polarity, uint32_t* length);
+
+#ifdef __cplusplus
+}
+#endif

+ 121 - 0
lib/one_wire/ibutton/ibutton_key.c

@@ -0,0 +1,121 @@
+#include <furi.h>
+#include <one_wire/maxim_crc.h>
+#include "ibutton_key.h"
+
+struct iButtonKey {
+    uint8_t data[IBUTTON_KEY_DATA_SIZE];
+    char name[IBUTTON_KEY_NAME_SIZE];
+    iButtonKeyType type;
+};
+
+iButtonKey* ibutton_key_alloc() {
+    iButtonKey* key = malloc(sizeof(iButtonKey));
+    memset(key, 0, sizeof(iButtonKey));
+    return key;
+}
+
+void ibutton_key_free(iButtonKey* key) {
+    free(key);
+}
+
+void ibutton_key_set(iButtonKey* to, const iButtonKey* from) {
+    memcpy(to, from, sizeof(iButtonKey));
+}
+
+void ibutton_key_set_data(iButtonKey* key, uint8_t* data, uint8_t data_count) {
+    furi_check(data_count > 0);
+    furi_check(data_count <= IBUTTON_KEY_DATA_SIZE);
+
+    memset(key->data, 0, IBUTTON_KEY_DATA_SIZE);
+    memcpy(key->data, data, data_count);
+}
+
+void ibutton_key_clear_data(iButtonKey* key) {
+    memset(key->data, 0, IBUTTON_KEY_DATA_SIZE);
+}
+
+const uint8_t* ibutton_key_get_data_p(iButtonKey* key) {
+    return key->data;
+}
+
+uint8_t ibutton_key_get_data_size(iButtonKey* key) {
+    return ibutton_key_get_size_by_type(key->type);
+}
+
+void ibutton_key_set_name(iButtonKey* key, const char* name) {
+    strlcpy(key->name, name, IBUTTON_KEY_NAME_SIZE);
+}
+
+const char* ibutton_key_get_name_p(iButtonKey* key) {
+    return key->name;
+}
+
+void ibutton_key_set_type(iButtonKey* key, iButtonKeyType key_type) {
+    key->type = key_type;
+}
+
+iButtonKeyType ibutton_key_get_type(iButtonKey* key) {
+    return key->type;
+}
+
+const char* ibutton_key_get_string_by_type(iButtonKeyType key_type) {
+    switch(key_type) {
+    case iButtonKeyCyfral:
+        return "Cyfral";
+        break;
+    case iButtonKeyMetakom:
+        return "Metakom";
+        break;
+    case iButtonKeyDS1990:
+        return "Dallas";
+        break;
+    default:
+        furi_crash("Invalid iButton type");
+        return "";
+        break;
+    }
+}
+
+bool ibutton_key_get_type_by_string(const char* type_string, iButtonKeyType* key_type) {
+    if(strcmp(type_string, ibutton_key_get_string_by_type(iButtonKeyCyfral)) == 0) {
+        *key_type = iButtonKeyCyfral;
+    } else if(strcmp(type_string, ibutton_key_get_string_by_type(iButtonKeyMetakom)) == 0) {
+        *key_type = iButtonKeyMetakom;
+    } else if(strcmp(type_string, ibutton_key_get_string_by_type(iButtonKeyDS1990)) == 0) {
+        *key_type = iButtonKeyDS1990;
+    } else {
+        return false;
+    }
+
+    return true;
+}
+
+uint8_t ibutton_key_get_size_by_type(iButtonKeyType key_type) {
+    uint8_t size = 0;
+
+    switch(key_type) {
+    case iButtonKeyCyfral:
+        size = 2;
+        break;
+    case iButtonKeyMetakom:
+        size = 4;
+        break;
+    case iButtonKeyDS1990:
+        size = 8;
+        break;
+    }
+
+    return size;
+}
+
+uint8_t ibutton_key_get_max_size() {
+    return IBUTTON_KEY_DATA_SIZE;
+}
+
+bool ibutton_key_dallas_crc_is_valid(iButtonKey* key) {
+    return (maxim_crc8(key->data, 8, MAXIM_CRC8_INIT) == 0);
+}
+
+bool ibutton_key_dallas_is_1990_key(iButtonKey* key) {
+    return (key->data[0] == 0x01);
+}

+ 145 - 0
lib/one_wire/ibutton/ibutton_key.h

@@ -0,0 +1,145 @@
+/**
+ * @file ibutton_key.h
+ * 
+ * iButton key data holder
+ */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define IBUTTON_KEY_DATA_SIZE 8
+#define IBUTTON_KEY_NAME_SIZE 22
+
+typedef enum {
+    iButtonKeyDS1990,
+    iButtonKeyCyfral,
+    iButtonKeyMetakom,
+} iButtonKeyType;
+
+typedef struct iButtonKey iButtonKey;
+
+/**
+ * Allocate key
+ * @return iButtonKey* 
+ */
+iButtonKey* ibutton_key_alloc();
+
+/**
+ * Free key
+ * @param key 
+ */
+void ibutton_key_free(iButtonKey* key);
+
+/**
+ * Copy key
+ * @param to 
+ * @param from 
+ */
+void ibutton_key_set(iButtonKey* to, const iButtonKey* from);
+
+/**
+ * Set key data
+ * @param key 
+ * @param data 
+ * @param data_count 
+ */
+void ibutton_key_set_data(iButtonKey* key, uint8_t* data, uint8_t data_count);
+
+/**
+ * Clear key data
+ * @param key 
+ */
+void ibutton_key_clear_data(iButtonKey* key);
+
+/**
+ * Get pointer to key data
+ * @param key 
+ * @return const uint8_t* 
+ */
+const uint8_t* ibutton_key_get_data_p(iButtonKey* key);
+
+/**
+ * Get key data size
+ * @param key 
+ * @return uint8_t 
+ */
+uint8_t ibutton_key_get_data_size(iButtonKey* key);
+
+/**
+ * Set key name
+ * @param key 
+ * @param name 
+ */
+void ibutton_key_set_name(iButtonKey* key, const char* name);
+
+/**
+ * Get pointer to key name
+ * @param key 
+ * @return const char* 
+ */
+const char* ibutton_key_get_name_p(iButtonKey* key);
+
+/**
+ * Set key type
+ * @param key 
+ * @param key_type 
+ */
+void ibutton_key_set_type(iButtonKey* key, iButtonKeyType key_type);
+
+/**
+ * Get key type
+ * @param key 
+ * @return iButtonKeyType 
+ */
+iButtonKeyType ibutton_key_get_type(iButtonKey* key);
+
+/**
+ * Get type string from key type
+ * @param key_type 
+ * @return const char* 
+ */
+const char* ibutton_key_get_string_by_type(iButtonKeyType key_type);
+
+/**
+ * Get key type from string
+ * @param type_string 
+ * @param key_type 
+ * @return bool 
+ */
+bool ibutton_key_get_type_by_string(const char* type_string, iButtonKeyType* key_type);
+
+/**
+ * Get key data size from type
+ * @param key_type 
+ * @return uint8_t 
+ */
+uint8_t ibutton_key_get_size_by_type(iButtonKeyType key_type);
+
+/**
+ * Get max key size
+ * @return uint8_t 
+ */
+uint8_t ibutton_key_get_max_size();
+
+/**
+ * Check if CRC for onewire key is valid
+ * @param key 
+ * @return true 
+ * @return false 
+ */
+bool ibutton_key_dallas_crc_is_valid(iButtonKey* key);
+
+/**
+ * Check if onewire key is a DS1990 key
+ * @param key 
+ * @return true 
+ * @return false 
+ */
+bool ibutton_key_dallas_is_1990_key(iButtonKey* key);
+
+#ifdef __cplusplus
+}
+#endif

+ 28 - 0
lib/one_wire/ibutton/ibutton_key_command.h

@@ -0,0 +1,28 @@
+/**
+ * @file ibutton_key_command.h
+ * 
+ * List of misc commands for Dallas and blanks
+ */
+
+#pragma once
+
+#define RW1990_1_CMD_WRITE_RECORD_FLAG 0xD1
+#define RW1990_1_CMD_READ_RECORD_FLAG 0xB5
+#define RW1990_1_CMD_WRITE_ROM 0xD5
+
+#define RW1990_2_CMD_WRITE_RECORD_FLAG 0x1D
+#define RW1990_2_CMD_READ_RECORD_FLAG 0x1E
+#define RW1990_2_CMD_WRITE_ROM 0xD5
+
+#define TM2004_CMD_READ_STATUS 0xAA
+#define TM2004_CMD_READ_MEMORY 0xF0
+#define TM2004_CMD_WRITE_ROM 0x3C
+#define TM2004_CMD_FINALIZATION 0x35
+#define TM2004_ANSWER_READ_MEMORY 0xF5
+
+#define TM01_CMD_WRITE_RECORD_FLAG 0xC1
+#define TM01_CMD_WRITE_ROM 0xC5
+#define TM01_CMD_SWITCH_TO_CYFRAL 0xCA
+#define TM01_CMD_SWITCH_TO_METAKOM 0xCB
+
+#define DS1990_CMD_READ_ROM 0x33

+ 197 - 0
lib/one_wire/ibutton/ibutton_worker.c

@@ -0,0 +1,197 @@
+#include <furi.h>
+#include <furi_hal.h>
+#include <atomic.h>
+#include "ibutton_worker_i.h"
+
+typedef enum {
+    iButtonMessageEnd,
+    iButtonMessageStop,
+    iButtonMessageRead,
+    iButtonMessageWrite,
+    iButtonMessageEmulate,
+} iButtonMessageType;
+
+typedef struct {
+    iButtonMessageType type;
+    union {
+        iButtonKey* key;
+    } data;
+} iButtonMessage;
+
+static int32_t ibutton_worker_thread(void* thread_context);
+
+iButtonWorker* ibutton_worker_alloc() {
+    iButtonWorker* worker = malloc(sizeof(iButtonWorker));
+    worker->key_p = NULL;
+    worker->key_data = malloc(ibutton_key_get_max_size());
+    worker->host = onewire_host_alloc();
+    worker->slave = onewire_slave_alloc();
+    worker->writer = ibutton_writer_alloc(worker->host);
+    worker->device = onewire_device_alloc(0, 0, 0, 0, 0, 0, 0, 0);
+    worker->pulse_decoder = pulse_decoder_alloc();
+    worker->protocol_cyfral = protocol_cyfral_alloc();
+    worker->protocol_metakom = protocol_metakom_alloc();
+    worker->messages = osMessageQueueNew(1, sizeof(iButtonMessage), NULL);
+    worker->mode_index = iButtonWorkerIdle;
+    worker->last_dwt_value = 0;
+    worker->read_cb = NULL;
+    worker->write_cb = NULL;
+    worker->emulate_cb = NULL;
+    worker->cb_ctx = NULL;
+
+    worker->encoder_cyfral = encoder_cyfral_alloc();
+    worker->encoder_metakom = encoder_metakom_alloc();
+
+    worker->thread = furi_thread_alloc();
+    furi_thread_set_name(worker->thread, "ibutton_worker");
+    furi_thread_set_callback(worker->thread, ibutton_worker_thread);
+    furi_thread_set_context(worker->thread, worker);
+    furi_thread_set_stack_size(worker->thread, 2048);
+
+    pulse_decoder_add_protocol(
+        worker->pulse_decoder,
+        protocol_cyfral_get_protocol(worker->protocol_cyfral),
+        PulseProtocolCyfral);
+    pulse_decoder_add_protocol(
+        worker->pulse_decoder,
+        protocol_metakom_get_protocol(worker->protocol_metakom),
+        PulseProtocolMetakom);
+
+    return worker;
+}
+
+void ibutton_worker_read_set_callback(
+    iButtonWorker* worker,
+    iButtonWorkerReadCallback callback,
+    void* context) {
+    furi_check(worker->mode_index == iButtonWorkerIdle);
+    worker->read_cb = callback;
+    worker->cb_ctx = context;
+}
+
+void ibutton_worker_write_set_callback(
+    iButtonWorker* worker,
+    iButtonWorkerWriteCallback callback,
+    void* context) {
+    furi_check(worker->mode_index == iButtonWorkerIdle);
+    worker->write_cb = callback;
+    worker->cb_ctx = context;
+}
+
+void ibutton_worker_emulate_set_callback(
+    iButtonWorker* worker,
+    iButtonWorkerEmulateCallback callback,
+    void* context) {
+    furi_check(worker->mode_index == iButtonWorkerIdle);
+    worker->emulate_cb = callback;
+    worker->cb_ctx = context;
+}
+
+void ibutton_worker_read_start(iButtonWorker* worker, iButtonKey* key) {
+    iButtonMessage message = {.type = iButtonMessageRead, .data.key = key};
+    furi_check(osMessageQueuePut(worker->messages, &message, 0, osWaitForever) == osOK);
+}
+
+void ibutton_worker_write_start(iButtonWorker* worker, iButtonKey* key) {
+    iButtonMessage message = {.type = iButtonMessageWrite, .data.key = key};
+    furi_check(osMessageQueuePut(worker->messages, &message, 0, osWaitForever) == osOK);
+}
+
+void ibutton_worker_emulate_start(iButtonWorker* worker, iButtonKey* key) {
+    iButtonMessage message = {.type = iButtonMessageEmulate, .data.key = key};
+    furi_check(osMessageQueuePut(worker->messages, &message, 0, osWaitForever) == osOK);
+}
+
+void ibutton_worker_stop(iButtonWorker* worker) {
+    iButtonMessage message = {.type = iButtonMessageStop};
+    furi_check(osMessageQueuePut(worker->messages, &message, 0, osWaitForever) == osOK);
+}
+
+void ibutton_worker_free(iButtonWorker* worker) {
+    pulse_decoder_free(worker->pulse_decoder);
+    protocol_metakom_free(worker->protocol_metakom);
+    protocol_cyfral_free(worker->protocol_cyfral);
+
+    ibutton_writer_free(worker->writer);
+
+    onewire_slave_free(worker->slave);
+
+    onewire_host_free(worker->host);
+    onewire_device_free(worker->device);
+
+    encoder_cyfral_free(worker->encoder_cyfral);
+    encoder_metakom_free(worker->encoder_metakom);
+
+    osMessageQueueDelete(worker->messages);
+
+    furi_thread_free(worker->thread);
+    free(worker->key_data);
+    free(worker);
+}
+
+void ibutton_worker_start_thread(iButtonWorker* worker) {
+    furi_thread_start(worker->thread);
+}
+
+void ibutton_worker_stop_thread(iButtonWorker* worker) {
+    iButtonMessage message = {.type = iButtonMessageEnd};
+    furi_check(osMessageQueuePut(worker->messages, &message, 0, osWaitForever) == osOK);
+    furi_thread_join(worker->thread);
+}
+
+void ibutton_worker_switch_mode(iButtonWorker* worker, iButtonWorkerMode mode) {
+    ibutton_worker_modes[worker->mode_index].stop(worker);
+    worker->mode_index = mode;
+    ibutton_worker_modes[worker->mode_index].start(worker);
+}
+
+void ibutton_worker_set_key_p(iButtonWorker* worker, iButtonKey* key) {
+    worker->key_p = key;
+}
+
+static int32_t ibutton_worker_thread(void* thread_context) {
+    iButtonWorker* worker = thread_context;
+    bool running = true;
+    iButtonMessage message;
+    osStatus_t status;
+
+    ibutton_worker_modes[worker->mode_index].start(worker);
+
+    while(running) {
+        status = osMessageQueueGet(
+            worker->messages, &message, NULL, ibutton_worker_modes[worker->mode_index].quant);
+        if(status == osOK) {
+            switch(message.type) {
+            case iButtonMessageEnd:
+                ibutton_worker_switch_mode(worker, iButtonWorkerIdle);
+                ibutton_worker_set_key_p(worker, NULL);
+                running = false;
+                break;
+            case iButtonMessageStop:
+                ibutton_worker_switch_mode(worker, iButtonWorkerIdle);
+                ibutton_worker_set_key_p(worker, NULL);
+                break;
+            case iButtonMessageRead:
+                ibutton_worker_set_key_p(worker, message.data.key);
+                ibutton_worker_switch_mode(worker, iButtonWorkerRead);
+                break;
+            case iButtonMessageWrite:
+                ibutton_worker_set_key_p(worker, message.data.key);
+                ibutton_worker_switch_mode(worker, iButtonWorkerWrite);
+                break;
+            case iButtonMessageEmulate:
+                ibutton_worker_set_key_p(worker, message.data.key);
+                ibutton_worker_switch_mode(worker, iButtonWorkerEmulate);
+                break;
+            }
+        } else if(status == osErrorTimeout) {
+            ibutton_worker_modes[worker->mode_index].tick(worker);
+        } else {
+            furi_crash("iButton worker error");
+        }
+    }
+
+    ibutton_worker_modes[worker->mode_index].stop(worker);
+
+    return 0;
+}

+ 113 - 0
lib/one_wire/ibutton/ibutton_worker.h

@@ -0,0 +1,113 @@
+/**
+ * @file ibutton_worker.h
+ * 
+ * iButton worker
+ */
+
+#pragma once
+#include "ibutton_key.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    iButtonWorkerWriteOK,
+    iButtonWorkerWriteSameKey,
+    iButtonWorkerWriteNoDetect,
+    iButtonWorkerWriteCannotWrite,
+} iButtonWorkerWriteResult;
+
+typedef void (*iButtonWorkerReadCallback)(void* context);
+typedef void (*iButtonWorkerWriteCallback)(void* context, iButtonWorkerWriteResult result);
+typedef void (*iButtonWorkerEmulateCallback)(void* context, bool emulated);
+
+typedef struct iButtonWorker iButtonWorker;
+
+/**
+ * Allocate ibutton worker
+ * @return iButtonWorker* 
+ */
+iButtonWorker* ibutton_worker_alloc();
+
+/**
+ * Free ibutton worker
+ * @param worker 
+ */
+void ibutton_worker_free(iButtonWorker* worker);
+
+/**
+ * Start ibutton worker thread
+ * @param worker 
+ */
+void ibutton_worker_start_thread(iButtonWorker* worker);
+
+/**
+ * Stop ibutton worker thread
+ * @param worker 
+ */
+void ibutton_worker_stop_thread(iButtonWorker* worker);
+
+/**
+ * Set "read success" callback
+ * @param worker 
+ * @param callback 
+ * @param context 
+ */
+void ibutton_worker_read_set_callback(
+    iButtonWorker* worker,
+    iButtonWorkerReadCallback callback,
+    void* context);
+
+/**
+ * Start read mode
+ * @param worker 
+ * @param key 
+ */
+void ibutton_worker_read_start(iButtonWorker* worker, iButtonKey* key);
+
+/**
+ * Set "write event" callback
+ * @param worker 
+ * @param callback 
+ * @param context 
+ */
+void ibutton_worker_write_set_callback(
+    iButtonWorker* worker,
+    iButtonWorkerWriteCallback callback,
+    void* context);
+
+/**
+ * Start write mode
+ * @param worker 
+ * @param key 
+ */
+void ibutton_worker_write_start(iButtonWorker* worker, iButtonKey* key);
+
+/**
+ * Set "emulate success" callback
+ * @param worker 
+ * @param callback 
+ * @param context 
+ */
+void ibutton_worker_emulate_set_callback(
+    iButtonWorker* worker,
+    iButtonWorkerEmulateCallback callback,
+    void* context);
+
+/**
+ * Start emulate mode
+ * @param worker 
+ * @param key 
+ */
+void ibutton_worker_emulate_start(iButtonWorker* worker, iButtonKey* key);
+
+/**
+ * Stop all modes
+ * @param worker 
+ */
+void ibutton_worker_stop(iButtonWorker* worker);
+
+#ifdef __cplusplus
+}
+#endif

+ 79 - 0
lib/one_wire/ibutton/ibutton_worker_i.h

@@ -0,0 +1,79 @@
+/**
+ * @file ibutton_worker_i.h
+ * 
+ * iButton worker, internal definitions 
+ */
+
+#pragma once
+#include "ibutton_worker.h"
+#include "ibutton_writer.h"
+#include "../one_wire_host.h"
+#include "../one_wire_slave.h"
+#include "../one_wire_device.h"
+#include "../pulse_protocols/pulse_decoder.h"
+#include "pulse_protocols/protocol_cyfral.h"
+#include "pulse_protocols/protocol_metakom.h"
+#include "encoder/encoder_cyfral.h"
+#include "encoder/encoder_metakom.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    PulseProtocolCyfral,
+    PulseProtocolMetakom,
+} PulseProtocols;
+
+typedef struct {
+    const uint32_t quant;
+    const void (*start)(iButtonWorker* worker);
+    const void (*tick)(iButtonWorker* worker);
+    const void (*stop)(iButtonWorker* worker);
+} iButtonWorkerModeType;
+
+typedef enum {
+    iButtonWorkerIdle = 0,
+    iButtonWorkerRead = 1,
+    iButtonWorkerWrite = 2,
+    iButtonWorkerEmulate = 3,
+} iButtonWorkerMode;
+
+typedef enum {
+    iButtonEmulateModeCyfral,
+    iButtonEmulateModeMetakom,
+} iButtonEmulateMode;
+
+struct iButtonWorker {
+    iButtonKey* key_p;
+    uint8_t* key_data;
+    OneWireHost* host;
+    OneWireSlave* slave;
+    OneWireDevice* device;
+    iButtonWriter* writer;
+    iButtonWorkerMode mode_index;
+    osMessageQueueId_t messages;
+    FuriThread* thread;
+
+    PulseDecoder* pulse_decoder;
+    ProtocolCyfral* protocol_cyfral;
+    ProtocolMetakom* protocol_metakom;
+    uint32_t last_dwt_value;
+
+    iButtonWorkerReadCallback read_cb;
+    iButtonWorkerWriteCallback write_cb;
+    iButtonWorkerEmulateCallback emulate_cb;
+    void* cb_ctx;
+
+    EncoderCyfral* encoder_cyfral;
+    EncoderMetakom* encoder_metakom;
+    iButtonEmulateMode emulate_mode;
+};
+
+extern const iButtonWorkerModeType ibutton_worker_modes[];
+
+void ibutton_worker_switch_mode(iButtonWorker* worker, iButtonWorkerMode mode);
+
+#ifdef __cplusplus
+}
+#endif

+ 330 - 0
lib/one_wire/ibutton/ibutton_worker_modes.c

@@ -0,0 +1,330 @@
+#include <furi.h>
+#include <furi_hal.h>
+#include "ibutton_worker_i.h"
+#include "ibutton_key_command.h"
+
+void ibutton_worker_mode_idle_start(iButtonWorker* worker);
+void ibutton_worker_mode_idle_tick(iButtonWorker* worker);
+void ibutton_worker_mode_idle_stop(iButtonWorker* worker);
+
+void ibutton_worker_mode_emulate_start(iButtonWorker* worker);
+void ibutton_worker_mode_emulate_tick(iButtonWorker* worker);
+void ibutton_worker_mode_emulate_stop(iButtonWorker* worker);
+
+void ibutton_worker_mode_read_start(iButtonWorker* worker);
+void ibutton_worker_mode_read_tick(iButtonWorker* worker);
+void ibutton_worker_mode_read_stop(iButtonWorker* worker);
+
+void ibutton_worker_mode_write_start(iButtonWorker* worker);
+void ibutton_worker_mode_write_tick(iButtonWorker* worker);
+void ibutton_worker_mode_write_stop(iButtonWorker* worker);
+
+const iButtonWorkerModeType ibutton_worker_modes[] = {
+    {
+        .quant = osWaitForever,
+        .start = ibutton_worker_mode_idle_start,
+        .tick = ibutton_worker_mode_idle_tick,
+        .stop = ibutton_worker_mode_idle_stop,
+    },
+    {
+        .quant = 100,
+        .start = ibutton_worker_mode_read_start,
+        .tick = ibutton_worker_mode_read_tick,
+        .stop = ibutton_worker_mode_read_stop,
+    },
+    {
+        .quant = 1000,
+        .start = ibutton_worker_mode_write_start,
+        .tick = ibutton_worker_mode_write_tick,
+        .stop = ibutton_worker_mode_write_stop,
+    },
+    {
+        .quant = 1000,
+        .start = ibutton_worker_mode_emulate_start,
+        .tick = ibutton_worker_mode_emulate_tick,
+        .stop = ibutton_worker_mode_emulate_stop,
+    },
+};
+
+/*********************** IDLE ***********************/
+
+void ibutton_worker_mode_idle_start(iButtonWorker* worker) {
+}
+
+void ibutton_worker_mode_idle_tick(iButtonWorker* worker) {
+}
+
+void ibutton_worker_mode_idle_stop(iButtonWorker* worker) {
+}
+
+/*********************** READ ***********************/
+
+void ibutton_worker_comparator_callback(bool level, void* context) {
+    iButtonWorker* worker = context;
+
+    uint32_t current_dwt_value = DWT->CYCCNT;
+
+    pulse_decoder_process_pulse(
+        worker->pulse_decoder, level, current_dwt_value - worker->last_dwt_value);
+
+    worker->last_dwt_value = current_dwt_value;
+}
+
+bool ibutton_worker_read_comparator(iButtonWorker* worker) {
+    bool result = false;
+
+    pulse_decoder_reset(worker->pulse_decoder);
+
+    furi_hal_rfid_pins_reset();
+    // pulldown pull pin, we sense the signal through the analog part of the RFID schematic
+    furi_hal_rfid_pin_pull_pulldown();
+    furi_hal_rfid_comp_set_callback(ibutton_worker_comparator_callback, worker);
+    worker->last_dwt_value = DWT->CYCCNT;
+    furi_hal_rfid_comp_start();
+
+    // TODO: rework with thread events, "pulse_decoder_get_decoded_index_with_timeout"
+    delay(100);
+    int32_t decoded_index = pulse_decoder_get_decoded_index(worker->pulse_decoder);
+    if(decoded_index >= 0) {
+        pulse_decoder_get_data(
+            worker->pulse_decoder, decoded_index, worker->key_data, ibutton_key_get_max_size());
+    }
+
+    switch(decoded_index) {
+    case PulseProtocolCyfral:
+        furi_check(worker->key_p != NULL);
+        ibutton_key_set_type(worker->key_p, iButtonKeyCyfral);
+        ibutton_key_set_data(worker->key_p, worker->key_data, ibutton_key_get_max_size());
+        result = true;
+        break;
+    case PulseProtocolMetakom:
+        furi_check(worker->key_p != NULL);
+        ibutton_key_set_type(worker->key_p, iButtonKeyMetakom);
+        ibutton_key_set_data(worker->key_p, worker->key_data, ibutton_key_get_max_size());
+        result = true;
+        break;
+        break;
+    default:
+        break;
+    }
+
+    furi_hal_rfid_comp_stop();
+    furi_hal_rfid_comp_set_callback(NULL, NULL);
+    furi_hal_rfid_pins_reset();
+
+    return result;
+}
+
+bool ibutton_worker_read_dallas(iButtonWorker* worker) {
+    bool result = false;
+    onewire_host_start(worker->host);
+    delay(100);
+    FURI_CRITICAL_ENTER();
+    if(onewire_host_search(worker->host, worker->key_data, NORMAL_SEARCH)) {
+        onewire_host_reset_search(worker->host);
+
+        // key found, verify
+        if(onewire_host_reset(worker->host)) {
+            onewire_host_write(worker->host, DS1990_CMD_READ_ROM);
+            bool key_valid = true;
+            for(uint8_t i = 0; i < ibutton_key_get_max_size(); i++) {
+                if(onewire_host_read(worker->host) != worker->key_data[i]) {
+                    key_valid = false;
+                    break;
+                }
+            }
+
+            if(key_valid) {
+                result = true;
+
+                furi_check(worker->key_p != NULL);
+                ibutton_key_set_type(worker->key_p, iButtonKeyDS1990);
+                ibutton_key_set_data(worker->key_p, worker->key_data, ibutton_key_get_max_size());
+            }
+        }
+    } else {
+        onewire_host_reset_search(worker->host);
+    }
+    onewire_host_stop(worker->host);
+    FURI_CRITICAL_EXIT();
+    return result;
+}
+
+void ibutton_worker_mode_read_start(iButtonWorker* worker) {
+    furi_hal_power_enable_otg();
+}
+
+void ibutton_worker_mode_read_tick(iButtonWorker* worker) {
+    bool valid = false;
+    if(ibutton_worker_read_dallas(worker)) {
+        valid = true;
+    } else if(ibutton_worker_read_comparator(worker)) {
+        valid = true;
+    }
+
+    if(valid) {
+        if(worker->read_cb != NULL) {
+            worker->read_cb(worker->cb_ctx);
+        }
+
+        ibutton_worker_switch_mode(worker, iButtonWorkerIdle);
+    }
+}
+
+void ibutton_worker_mode_read_stop(iButtonWorker* worker) {
+    furi_hal_power_disable_otg();
+}
+
+/*********************** EMULATE ***********************/
+static void onewire_slave_callback(void* context) {
+    furi_assert(context);
+    iButtonWorker* worker = context;
+    if(worker->emulate_cb != NULL) {
+        worker->emulate_cb(worker->cb_ctx, true);
+    }
+}
+
+void ibutton_worker_emulate_dallas_start(iButtonWorker* worker) {
+    uint8_t* device_id = onewire_device_get_id_p(worker->device);
+    const uint8_t* key_id = ibutton_key_get_data_p(worker->key_p);
+    const uint8_t key_size = ibutton_key_get_max_size();
+    memcpy(device_id, key_id, key_size);
+
+    onewire_slave_attach(worker->slave, worker->device);
+    onewire_slave_start(worker->slave);
+    onewire_slave_set_result_callback(worker->slave, onewire_slave_callback, worker);
+}
+
+void ibutton_worker_emulate_dallas_stop(iButtonWorker* worker) {
+    onewire_slave_stop(worker->slave);
+    onewire_slave_detach(worker->slave);
+}
+
+void ibutton_worker_emulate_timer_cb(void* context) {
+    furi_assert(context);
+    iButtonWorker* worker = context;
+
+    bool polarity;
+    uint32_t length;
+
+    switch(worker->emulate_mode) {
+    case iButtonEmulateModeCyfral:
+        encoder_cyfral_get_pulse(worker->encoder_cyfral, &polarity, &length);
+        break;
+    case iButtonEmulateModeMetakom:
+        encoder_metakom_get_pulse(worker->encoder_metakom, &polarity, &length);
+        break;
+    }
+
+    furi_hal_ibutton_emulate_set_next(length);
+
+    if(polarity) {
+        furi_hal_ibutton_pin_high();
+    } else {
+        furi_hal_ibutton_pin_low();
+    }
+}
+
+void ibutton_worker_emulate_timer_start(iButtonWorker* worker) {
+    furi_assert(worker->key_p);
+    const uint8_t* key_id = ibutton_key_get_data_p(worker->key_p);
+    const uint8_t key_size = ibutton_key_get_max_size();
+
+    switch(ibutton_key_get_type(worker->key_p)) {
+    case iButtonKeyDS1990:
+        return;
+        break;
+    case iButtonKeyCyfral:
+        worker->emulate_mode = iButtonEmulateModeCyfral;
+        encoder_cyfral_reset(worker->encoder_cyfral);
+        encoder_cyfral_set_data(worker->encoder_cyfral, key_id, key_size);
+        break;
+    case iButtonKeyMetakom:
+        worker->emulate_mode = iButtonEmulateModeMetakom;
+        encoder_metakom_reset(worker->encoder_metakom);
+        encoder_metakom_set_data(worker->encoder_metakom, key_id, key_size);
+        break;
+    }
+
+    furi_hal_ibutton_start_drive();
+    furi_hal_ibutton_emulate_start(0, ibutton_worker_emulate_timer_cb, worker);
+}
+
+void ibutton_worker_emulate_timer_stop(iButtonWorker* worker) {
+    furi_hal_ibutton_emulate_stop();
+}
+
+void ibutton_worker_mode_emulate_start(iButtonWorker* worker) {
+    furi_assert(worker->key_p);
+
+    furi_hal_rfid_pins_reset();
+    furi_hal_rfid_pin_pull_pulldown();
+
+    switch(ibutton_key_get_type(worker->key_p)) {
+    case iButtonKeyDS1990:
+        ibutton_worker_emulate_dallas_start(worker);
+        break;
+    case iButtonKeyCyfral:
+    case iButtonKeyMetakom:
+        ibutton_worker_emulate_timer_start(worker);
+        break;
+    }
+}
+
+void ibutton_worker_mode_emulate_tick(iButtonWorker* worker) {
+}
+
+void ibutton_worker_mode_emulate_stop(iButtonWorker* worker) {
+    furi_assert(worker->key_p);
+
+    furi_hal_rfid_pins_reset();
+
+    switch(ibutton_key_get_type(worker->key_p)) {
+    case iButtonKeyDS1990:
+        ibutton_worker_emulate_dallas_stop(worker);
+        break;
+    case iButtonKeyCyfral:
+    case iButtonKeyMetakom:
+        ibutton_worker_emulate_timer_stop(worker);
+        break;
+    }
+}
+
+/*********************** WRITE ***********************/
+
+void ibutton_worker_mode_write_start(iButtonWorker* worker) {
+    furi_hal_power_enable_otg();
+    onewire_host_start(worker->host);
+}
+
+void ibutton_worker_mode_write_tick(iButtonWorker* worker) {
+    furi_check(worker->key_p != NULL);
+    iButtonWriterResult writer_result = ibutton_writer_write(worker->writer, worker->key_p);
+    iButtonWorkerWriteResult result;
+    switch(writer_result) {
+    case iButtonWriterOK:
+        result = iButtonWorkerWriteOK;
+        break;
+    case iButtonWriterSameKey:
+        result = iButtonWorkerWriteSameKey;
+        break;
+    case iButtonWriterNoDetect:
+        result = iButtonWorkerWriteNoDetect;
+        break;
+    case iButtonWriterCannotWrite:
+        result = iButtonWorkerWriteCannotWrite;
+        break;
+    default:
+        result = iButtonWorkerWriteNoDetect;
+        break;
+    }
+
+    if(worker->write_cb != NULL) {
+        worker->write_cb(worker->cb_ctx, result);
+    }
+}
+
+void ibutton_worker_mode_write_stop(iButtonWorker* worker) {
+    furi_hal_power_disable_otg();
+    onewire_host_stop(worker->host);
+}

+ 298 - 0
lib/one_wire/ibutton/ibutton_writer.c

@@ -0,0 +1,298 @@
+#include <furi.h>
+#include <furi_hal.h>
+#include "ibutton_writer.h"
+#include "ibutton_key_command.h"
+
+/*********************** PRIVATE ***********************/
+
+struct iButtonWriter {
+    OneWireHost* host;
+};
+
+static void writer_write_one_bit(iButtonWriter* writer, bool value, uint32_t delay) {
+    onewire_host_write_bit(writer->host, value);
+    delay_us(delay);
+}
+
+static void writer_write_byte_ds1990(iButtonWriter* writer, uint8_t data) {
+    for(uint8_t n_bit = 0; n_bit < 8; n_bit++) {
+        onewire_host_write_bit(writer->host, data & 1);
+        delay_us(5000);
+        data = data >> 1;
+    }
+}
+
+static bool writer_compare_key_ds1990(iButtonWriter* writer, iButtonKey* key) {
+    bool result = false;
+
+    if(ibutton_key_get_type(key) == iButtonKeyDS1990) {
+        FURI_CRITICAL_ENTER();
+        bool presence = onewire_host_reset(writer->host);
+
+        if(presence) {
+            onewire_host_write(writer->host, DS1990_CMD_READ_ROM);
+
+            result = true;
+            for(uint8_t i = 0; i < ibutton_key_get_data_size(key); i++) {
+                if(ibutton_key_get_data_p(key)[i] != onewire_host_read(writer->host)) {
+                    result = false;
+                    break;
+                }
+            }
+        }
+
+        FURI_CRITICAL_EXIT();
+    }
+
+    return result;
+}
+
+static bool writer_write_TM2004(iButtonWriter* writer, iButtonKey* key) {
+    uint8_t answer;
+    bool result = true;
+
+    if(ibutton_key_get_type(key) == iButtonKeyDS1990) {
+        FURI_CRITICAL_ENTER();
+
+        // write rom, addr is 0x0000
+        onewire_host_reset(writer->host);
+        onewire_host_write(writer->host, TM2004_CMD_WRITE_ROM);
+        onewire_host_write(writer->host, 0x00);
+        onewire_host_write(writer->host, 0x00);
+
+        // write key
+        for(uint8_t i = 0; i < ibutton_key_get_data_size(key); i++) {
+            // write key byte
+            onewire_host_write(writer->host, ibutton_key_get_data_p(key)[i]);
+            answer = onewire_host_read(writer->host);
+            // TODO: check answer CRC
+
+            // pulse indicating that data is correct
+            delay_us(600);
+            writer_write_one_bit(writer, 1, 50000);
+
+            // read writed key byte
+            answer = onewire_host_read(writer->host);
+
+            // check that writed and readed are same
+            if(ibutton_key_get_data_p(key)[i] != answer) {
+                result = false;
+                break;
+            }
+        }
+
+        if(!writer_compare_key_ds1990(writer, key)) {
+            result = false;
+        }
+
+        onewire_host_reset(writer->host);
+
+        FURI_CRITICAL_EXIT();
+    } else {
+        result = false;
+    }
+
+    return result;
+}
+
+static bool writer_write_1990_1(iButtonWriter* writer, iButtonKey* key) {
+    bool result = false;
+
+    if(ibutton_key_get_type(key) == iButtonKeyDS1990) {
+        FURI_CRITICAL_ENTER();
+
+        // unlock
+        onewire_host_reset(writer->host);
+        onewire_host_write(writer->host, RW1990_1_CMD_WRITE_RECORD_FLAG);
+        delay_us(10);
+        writer_write_one_bit(writer, 0, 5000);
+
+        // write key
+        onewire_host_reset(writer->host);
+        onewire_host_write(writer->host, RW1990_1_CMD_WRITE_ROM);
+        for(uint8_t i = 0; i < ibutton_key_get_data_size(key); i++) {
+            // inverted key for RW1990.1
+            writer_write_byte_ds1990(writer, ~ibutton_key_get_data_p(key)[i]);
+            delay_us(30000);
+        }
+
+        // lock
+        onewire_host_write(writer->host, RW1990_1_CMD_WRITE_RECORD_FLAG);
+        writer_write_one_bit(writer, 1, 10000);
+
+        FURI_CRITICAL_EXIT();
+
+        if(writer_compare_key_ds1990(writer, key)) {
+            result = true;
+        }
+    }
+
+    return result;
+}
+
+static bool writer_write_1990_2(iButtonWriter* writer, iButtonKey* key) {
+    bool result = false;
+
+    if(ibutton_key_get_type(key) == iButtonKeyDS1990) {
+        FURI_CRITICAL_ENTER();
+
+        // unlock
+        onewire_host_reset(writer->host);
+        onewire_host_write(writer->host, RW1990_2_CMD_WRITE_RECORD_FLAG);
+        delay_us(10);
+        writer_write_one_bit(writer, 1, 5000);
+
+        // write key
+        onewire_host_reset(writer->host);
+        onewire_host_write(writer->host, RW1990_2_CMD_WRITE_ROM);
+        for(uint8_t i = 0; i < ibutton_key_get_data_size(key); i++) {
+            writer_write_byte_ds1990(writer, ibutton_key_get_data_p(key)[i]);
+            delay_us(30000);
+        }
+
+        // lock
+        onewire_host_write(writer->host, RW1990_2_CMD_WRITE_RECORD_FLAG);
+        writer_write_one_bit(writer, 0, 10000);
+
+        FURI_CRITICAL_EXIT();
+
+        if(writer_compare_key_ds1990(writer, key)) {
+            result = true;
+        }
+    }
+
+    return result;
+}
+
+/*
+// TODO: adapt and test
+static bool writer_write_TM01(
+    iButtonWriter* writer,
+    iButtonKey type,
+    const uint8_t* key,
+    uint8_t key_length) {
+    bool result = true;
+
+    {
+        // TODO test and encoding
+        FURI_CRITICAL_ENTER();
+
+        // unlock
+        onewire_host_reset(writer->host);
+        onewire_host_write(writer->host, TM01::CMD_WRITE_RECORD_FLAG);
+        onewire_write_one_bit(1, 10000);
+
+        // write key
+        onewire_host_reset(writer->host);
+        onewire_host_write(writer->host, TM01::CMD_WRITE_ROM);
+
+        // TODO: key types
+        //if(type == KEY_METAKOM || type == KEY_CYFRAL) {
+        //} else {
+        for(uint8_t i = 0; i < key->get_type_data_size(); i++) {
+            write_byte_ds1990(key->get_data()[i]);
+            delay_us(10000);
+        }
+        //}
+
+        // lock
+        onewire_host_write(writer->host, TM01::CMD_WRITE_RECORD_FLAG);
+        onewire_write_one_bit(0, 10000);
+
+        FURI_CRITICAL_EXIT();
+    }
+
+    if(!compare_key_ds1990(key)) {
+        result = false;
+    }
+
+    {
+        FURI_CRITICAL_ENTER();
+
+        if(key->get_key_type() == iButtonKeyType::KeyMetakom ||
+           key->get_key_type() == iButtonKeyType::KeyCyfral) {
+            onewire_host_reset(writer->host);
+            if(key->get_key_type() == iButtonKeyType::KeyCyfral)
+                onewire_host_write(writer->host, TM01::CMD_SWITCH_TO_CYFRAL);
+            else
+                onewire_host_write(writer->host, TM01::CMD_SWITCH_TO_METAKOM);
+            onewire_write_one_bit(1);
+        }
+
+        FURI_CRITICAL_EXIT();
+    }
+
+    return result;
+}
+*/
+
+static iButtonWriterResult writer_write_DS1990(iButtonWriter* writer, iButtonKey* key) {
+    iButtonWriterResult result = iButtonWriterNoDetect;
+    bool same_key = writer_compare_key_ds1990(writer, key);
+
+    if(!same_key) {
+        // currently we can write:
+        // RW1990_1, TM08v2, TM08vi-2 by write_1990_1()
+        // RW1990_2 by write_1990_2()
+        // RW2004, RW2004, TM2004 with EEPROM by write_TM2004();
+
+        bool write_result = true;
+        do {
+            if(writer_write_1990_1(writer, key)) break;
+            if(writer_write_1990_2(writer, key)) break;
+            if(writer_write_TM2004(writer, key)) break;
+            write_result = false;
+        } while(false);
+
+        if(write_result) {
+            result = iButtonWriterOK;
+        } else {
+            result = iButtonWriterCannotWrite;
+        }
+    } else {
+        result = iButtonWriterSameKey;
+    }
+
+    return result;
+}
+
+/*********************** PUBLIC ***********************/
+
+iButtonWriter* ibutton_writer_alloc(OneWireHost* host) {
+    iButtonWriter* writer = malloc(sizeof(iButtonWriter));
+    writer->host = host;
+    return writer;
+}
+
+void ibutton_writer_free(iButtonWriter* writer) {
+    free(writer);
+}
+
+iButtonWriterResult ibutton_writer_write(iButtonWriter* writer, iButtonKey* key) {
+    iButtonWriterResult result = iButtonWriterNoDetect;
+
+    osKernelLock();
+    bool blank_present = onewire_host_reset(writer->host);
+    osKernelUnlock();
+
+    if(blank_present) {
+        switch(ibutton_key_get_type(key)) {
+        case iButtonKeyDS1990:
+            result = writer_write_DS1990(writer, key);
+        default:
+            break;
+        }
+    }
+
+    return result;
+}
+
+void ibutton_writer_start(iButtonWriter* writer) {
+    furi_hal_power_enable_otg();
+    onewire_host_start(writer->host);
+}
+
+void ibutton_writer_stop(iButtonWriter* writer) {
+    onewire_host_stop(writer->host);
+    furi_hal_power_disable_otg();
+}

+ 60 - 0
lib/one_wire/ibutton/ibutton_writer.h

@@ -0,0 +1,60 @@
+/**
+ * @file ibutton_writer.h
+ * 
+ * iButton blanks writer
+ */
+
+#pragma once
+#include <furi_hal_gpio.h>
+#include "ibutton_key.h"
+#include "../one_wire_host.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    iButtonWriterOK,
+    iButtonWriterSameKey,
+    iButtonWriterNoDetect,
+    iButtonWriterCannotWrite,
+} iButtonWriterResult;
+
+typedef struct iButtonWriter iButtonWriter;
+
+/**
+ * Allocate writer
+ * @param host 
+ * @return iButtonWriter* 
+ */
+iButtonWriter* ibutton_writer_alloc(OneWireHost* host);
+
+/**
+ * Deallocate writer
+ * @param writer 
+ */
+void ibutton_writer_free(iButtonWriter* writer);
+
+/**
+ * Write key to blank
+ * @param writer 
+ * @param key 
+ * @return iButtonWriterResult 
+ */
+iButtonWriterResult ibutton_writer_write(iButtonWriter* writer, iButtonKey* key);
+
+/**
+ * Start writing. Must be called before write attempt
+ * @param writer 
+ */
+void ibutton_writer_start(iButtonWriter* writer);
+
+/**
+ * Stop writing
+ * @param writer 
+ */
+void ibutton_writer_stop(iButtonWriter* writer);
+
+#ifdef __cplusplus
+}
+#endif

+ 256 - 0
lib/one_wire/ibutton/pulse_protocols/protocol_cyfral.c

@@ -0,0 +1,256 @@
+#include "protocol_cyfral.h"
+#include <stdlib.h>
+#include <string.h>
+#include <furi/check.h>
+#include <furi_hal_delay.h>
+
+#define CYFRAL_DATA_SIZE 2
+#define CYFRAL_MAX_PERIOD_US 230
+
+typedef enum {
+    CYFRAL_BIT_WAIT_FRONT_HIGH,
+    CYFRAL_BIT_WAIT_FRONT_LOW,
+} CyfralBitState;
+
+typedef enum {
+    CYFRAL_WAIT_START_NIBBLE,
+    CYFRAL_READ_NIBBLE,
+    CYFRAL_READ_STOP_NIBBLE,
+} CyfralState;
+
+struct ProtocolCyfral {
+    PulseProtocol* protocol;
+
+    CyfralState state;
+    CyfralBitState bit_state;
+
+    // ready flag, key is read and valid
+    // TODO: atomic access
+    bool ready;
+    // key data storage
+    uint16_t key_data;
+    // high + low period time
+    uint32_t period_time;
+    // temporary nibble storage
+    uint8_t nibble;
+    // data valid flag
+    // MUST be checked only in READ_STOP_NIBBLE state
+    bool data_valid;
+    // nibble index, we expect 8 nibbles
+    uint8_t index;
+    // bit index in nibble, 4 bit per nibble
+    uint8_t bit_index;
+    // max period, 230us x clock per us
+    uint32_t max_period;
+};
+
+static void cyfral_pulse(void* context, bool polarity, uint32_t length);
+static void cyfral_reset(void* context);
+static void cyfral_get_data(void* context, uint8_t* data, size_t length);
+static bool cyfral_decoded(void* context);
+
+ProtocolCyfral* protocol_cyfral_alloc() {
+    ProtocolCyfral* cyfral = malloc(sizeof(ProtocolCyfral));
+    cyfral_reset(cyfral);
+
+    cyfral->protocol = pulse_protocol_alloc();
+
+    pulse_protocol_set_context(cyfral->protocol, cyfral);
+    pulse_protocol_set_pulse_cb(cyfral->protocol, cyfral_pulse);
+    pulse_protocol_set_reset_cb(cyfral->protocol, cyfral_reset);
+    pulse_protocol_set_get_data_cb(cyfral->protocol, cyfral_get_data);
+    pulse_protocol_set_decoded_cb(cyfral->protocol, cyfral_decoded);
+
+    return cyfral;
+}
+
+void protocol_cyfral_free(ProtocolCyfral* cyfral) {
+    furi_assert(cyfral);
+    pulse_protocol_free(cyfral->protocol);
+    free(cyfral);
+}
+
+PulseProtocol* protocol_cyfral_get_protocol(ProtocolCyfral* cyfral) {
+    furi_assert(cyfral);
+    return cyfral->protocol;
+}
+
+static void cyfral_get_data(void* context, uint8_t* data, size_t length) {
+    furi_assert(context);
+    furi_check(length >= CYFRAL_DATA_SIZE);
+    ProtocolCyfral* cyfral = context;
+    memcpy(data, &cyfral->key_data, CYFRAL_DATA_SIZE);
+}
+
+static bool cyfral_decoded(void* context) {
+    furi_assert(context);
+    ProtocolCyfral* cyfral = context;
+    bool decoded = cyfral->ready;
+    return decoded;
+}
+
+static void cyfral_reset(void* context) {
+    furi_assert(context);
+    ProtocolCyfral* cyfral = context;
+    cyfral->state = CYFRAL_WAIT_START_NIBBLE;
+    cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_LOW;
+
+    cyfral->period_time = 0;
+    cyfral->bit_index = 0;
+    cyfral->ready = false;
+    cyfral->index = 0;
+
+    cyfral->key_data = 0;
+    cyfral->nibble = 0;
+    cyfral->data_valid = true;
+
+    cyfral->max_period = CYFRAL_MAX_PERIOD_US * instructions_per_us;
+}
+
+static bool cyfral_process_bit(
+    ProtocolCyfral* cyfral,
+    bool polarity,
+    uint32_t length,
+    bool* bit_ready,
+    bool* bit_value) {
+    bool result = true;
+    *bit_ready = false;
+
+    // bit start from low
+    switch(cyfral->bit_state) {
+    case CYFRAL_BIT_WAIT_FRONT_LOW:
+        if(polarity == true) {
+            cyfral->period_time += length;
+
+            *bit_ready = true;
+            if(cyfral->period_time <= cyfral->max_period) {
+                if((cyfral->period_time / 2) > length) {
+                    *bit_value = false;
+                } else {
+                    *bit_value = true;
+                }
+            } else {
+                result = false;
+            }
+
+            cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_HIGH;
+        } else {
+            result = false;
+        }
+        break;
+    case CYFRAL_BIT_WAIT_FRONT_HIGH:
+        if(polarity == false) {
+            cyfral->period_time = length;
+            cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_LOW;
+        } else {
+            result = false;
+        }
+        break;
+    }
+
+    return result;
+}
+
+static void cyfral_pulse(void* context, bool polarity, uint32_t length) {
+    furi_assert(context);
+    ProtocolCyfral* cyfral = context;
+
+    bool bit_ready;
+    bool bit_value;
+
+    if(cyfral->ready) return;
+
+    switch(cyfral->state) {
+    case CYFRAL_WAIT_START_NIBBLE:
+        // wait for start word
+        if(cyfral_process_bit(cyfral, polarity, length, &bit_ready, &bit_value)) {
+            if(bit_ready) {
+                cyfral->nibble = ((cyfral->nibble << 1) | bit_value) & 0x0F;
+                if(cyfral->nibble == 0b0001) {
+                    cyfral->nibble = 0;
+                    cyfral->state = CYFRAL_READ_NIBBLE;
+                }
+            }
+        } else {
+            cyfral_reset(cyfral);
+        }
+
+        break;
+    case CYFRAL_READ_NIBBLE:
+        // read nibbles
+        if(cyfral_process_bit(cyfral, polarity, length, &bit_ready, &bit_value)) {
+            if(bit_ready) {
+                cyfral->nibble = (cyfral->nibble << 1) | bit_value;
+
+                cyfral->bit_index++;
+
+                //convert every nibble to 2-bit index
+                if(cyfral->bit_index == 4) {
+                    switch(cyfral->nibble) {
+                    case 0b1110:
+                        cyfral->key_data = (cyfral->key_data << 2) | 0b11;
+                        break;
+                    case 0b1101:
+                        cyfral->key_data = (cyfral->key_data << 2) | 0b10;
+                        break;
+                    case 0b1011:
+                        cyfral->key_data = (cyfral->key_data << 2) | 0b01;
+                        break;
+                    case 0b0111:
+                        cyfral->key_data = (cyfral->key_data << 2) | 0b00;
+                        break;
+                    default:
+                        cyfral->data_valid = false;
+                        break;
+                    }
+
+                    cyfral->nibble = 0;
+                    cyfral->bit_index = 0;
+                    cyfral->index++;
+                }
+
+                // succefully read 8 nibbles
+                if(cyfral->index == 8) {
+                    cyfral->state = CYFRAL_READ_STOP_NIBBLE;
+                }
+            }
+        } else {
+            cyfral_reset(cyfral);
+        }
+        break;
+    case CYFRAL_READ_STOP_NIBBLE:
+        // read stop nibble
+        if(cyfral_process_bit(cyfral, polarity, length, &bit_ready, &bit_value)) {
+            if(bit_ready) {
+                cyfral->nibble = ((cyfral->nibble << 1) | bit_value) & 0x0F;
+                cyfral->bit_index++;
+
+                switch(cyfral->bit_index) {
+                case 0:
+                case 1:
+                case 2:
+                case 3:
+                    break;
+                case 4:
+                    if(cyfral->nibble == 0b0001) {
+                        // validate data
+                        if(cyfral->data_valid) {
+                            cyfral->ready = true;
+                        } else {
+                            cyfral_reset(cyfral);
+                        }
+                    } else {
+                        cyfral_reset(cyfral);
+                    }
+                    break;
+                default:
+                    cyfral_reset(cyfral);
+                    break;
+                }
+            }
+        } else {
+            cyfral_reset(cyfral);
+        }
+        break;
+    }
+}

+ 38 - 0
lib/one_wire/ibutton/pulse_protocols/protocol_cyfral.h

@@ -0,0 +1,38 @@
+/**
+ * @file protocol_cyfral.h
+ * 
+ * Cyfral pulse format decoder
+ */
+
+#pragma once
+#include <stdint.h>
+#include "../../pulse_protocols/pulse_protocol.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct ProtocolCyfral ProtocolCyfral;
+
+/**
+ * Allocate decoder
+ * @return ProtocolCyfral* 
+ */
+ProtocolCyfral* protocol_cyfral_alloc();
+
+/**
+ * Deallocate decoder
+ * @param cyfral 
+ */
+void protocol_cyfral_free(ProtocolCyfral* cyfral);
+
+/**
+ * Get protocol interface
+ * @param cyfral 
+ * @return PulseProtocol* 
+ */
+PulseProtocol* protocol_cyfral_get_protocol(ProtocolCyfral* cyfral);
+
+#ifdef __cplusplus
+}
+#endif

+ 262 - 0
lib/one_wire/ibutton/pulse_protocols/protocol_metakom.c

@@ -0,0 +1,262 @@
+#include "protocol_metakom.h"
+#include <stdlib.h>
+#include <string.h>
+#include <furi/check.h>
+#include <furi_hal_delay.h>
+
+#define METAKOM_DATA_SIZE 4
+#define METAKOM_PERIOD_SAMPLE_COUNT 10
+
+typedef enum {
+    METAKOM_WAIT_PERIOD_SYNC,
+    METAKOM_WAIT_START_BIT,
+    METAKOM_WAIT_START_WORD,
+    METAKOM_READ_WORD,
+    METAKOM_READ_STOP_WORD,
+} MetakomState;
+
+typedef enum {
+    METAKOM_BIT_WAIT_FRONT_HIGH,
+    METAKOM_BIT_WAIT_FRONT_LOW,
+} MetakomBitState;
+
+struct ProtocolMetakom {
+    PulseProtocol* protocol;
+
+    // high + low period time
+    uint32_t period_time;
+    uint32_t low_time_storage;
+    uint8_t period_sample_index;
+    uint32_t period_sample_data[METAKOM_PERIOD_SAMPLE_COUNT];
+
+    // ready flag
+    // TODO: atomic access
+    bool ready;
+
+    uint8_t tmp_data;
+    uint8_t tmp_counter;
+
+    uint32_t key_data;
+    uint8_t key_data_index;
+
+    MetakomBitState bit_state;
+    MetakomState state;
+};
+
+static void metakom_pulse(void* context, bool polarity, uint32_t length);
+static void metakom_reset(void* context);
+static void metakom_get_data(void* context, uint8_t* data, size_t length);
+static bool metakom_decoded(void* context);
+
+ProtocolMetakom* protocol_metakom_alloc() {
+    ProtocolMetakom* metakom = malloc(sizeof(ProtocolMetakom));
+    metakom_reset(metakom);
+
+    metakom->protocol = pulse_protocol_alloc();
+
+    pulse_protocol_set_context(metakom->protocol, metakom);
+    pulse_protocol_set_pulse_cb(metakom->protocol, metakom_pulse);
+    pulse_protocol_set_reset_cb(metakom->protocol, metakom_reset);
+    pulse_protocol_set_get_data_cb(metakom->protocol, metakom_get_data);
+    pulse_protocol_set_decoded_cb(metakom->protocol, metakom_decoded);
+
+    return metakom;
+}
+
+void protocol_metakom_free(ProtocolMetakom* metakom) {
+    furi_assert(metakom);
+    pulse_protocol_free(metakom->protocol);
+    free(metakom);
+}
+
+PulseProtocol* protocol_metakom_get_protocol(ProtocolMetakom* metakom) {
+    furi_assert(metakom);
+    return metakom->protocol;
+}
+
+static void metakom_get_data(void* context, uint8_t* data, size_t length) {
+    furi_assert(context);
+    furi_check(length >= METAKOM_DATA_SIZE);
+    ProtocolMetakom* metakom = context;
+    memcpy(data, &metakom->key_data, METAKOM_DATA_SIZE);
+}
+
+static bool metakom_decoded(void* context) {
+    furi_assert(context);
+    ProtocolMetakom* metakom = context;
+    bool decoded = metakom->ready;
+    return decoded;
+}
+
+static void metakom_reset(void* context) {
+    furi_assert(context);
+    ProtocolMetakom* metakom = context;
+
+    metakom->ready = false;
+    metakom->period_sample_index = 0;
+    metakom->period_time = 0;
+    metakom->tmp_counter = 0;
+    metakom->tmp_data = 0;
+    for(uint8_t i = 0; i < METAKOM_PERIOD_SAMPLE_COUNT; i++) {
+        metakom->period_sample_data[i] = 0;
+    };
+    metakom->state = METAKOM_WAIT_PERIOD_SYNC;
+    metakom->bit_state = METAKOM_BIT_WAIT_FRONT_LOW;
+    metakom->key_data = 0;
+    metakom->key_data_index = 0;
+    metakom->low_time_storage = 0;
+}
+
+static bool metakom_parity_check(uint8_t data) {
+    uint8_t ones_count = 0;
+    bool result;
+
+    for(uint8_t i = 0; i < 8; i++) {
+        if((data >> i) & 0b00000001) {
+            ones_count++;
+        }
+    }
+
+    result = (ones_count % 2 == 0);
+
+    return result;
+}
+
+static bool metakom_process_bit(
+    ProtocolMetakom* metakom,
+    bool polarity,
+    uint32_t time,
+    uint32_t* high_time,
+    uint32_t* low_time) {
+    bool result = false;
+
+    switch(metakom->bit_state) {
+    case METAKOM_BIT_WAIT_FRONT_LOW:
+        if(polarity == false) {
+            *low_time = metakom->low_time_storage;
+            *high_time = time;
+            result = true;
+            metakom->bit_state = METAKOM_BIT_WAIT_FRONT_HIGH;
+        }
+        break;
+    case METAKOM_BIT_WAIT_FRONT_HIGH:
+        if(polarity == true) {
+            metakom->low_time_storage = time;
+            metakom->bit_state = METAKOM_BIT_WAIT_FRONT_LOW;
+        }
+        break;
+    }
+
+    return result;
+}
+
+static void metakom_pulse(void* context, bool polarity, uint32_t time) {
+    furi_assert(context);
+    ProtocolMetakom* metakom = context;
+
+    if(metakom->ready) return;
+
+    uint32_t high_time = 0;
+    uint32_t low_time = 0;
+
+    switch(metakom->state) {
+    case METAKOM_WAIT_PERIOD_SYNC:
+        if(metakom_process_bit(metakom, polarity, time, &high_time, &low_time)) {
+            metakom->period_sample_data[metakom->period_sample_index] = high_time + low_time;
+            metakom->period_sample_index++;
+
+            if(metakom->period_sample_index == METAKOM_PERIOD_SAMPLE_COUNT) {
+                for(uint8_t i = 0; i < METAKOM_PERIOD_SAMPLE_COUNT; i++) {
+                    metakom->period_time += metakom->period_sample_data[i];
+                };
+                metakom->period_time /= METAKOM_PERIOD_SAMPLE_COUNT;
+
+                metakom->state = METAKOM_WAIT_START_BIT;
+            }
+        }
+
+        break;
+    case METAKOM_WAIT_START_BIT:
+        if(metakom_process_bit(metakom, polarity, time, &high_time, &low_time)) {
+            metakom->tmp_counter++;
+            if(high_time > metakom->period_time) {
+                metakom->tmp_counter = 0;
+                metakom->state = METAKOM_WAIT_START_WORD;
+            }
+
+            if(metakom->tmp_counter > 40) {
+                metakom_reset(metakom);
+            }
+        }
+
+        break;
+    case METAKOM_WAIT_START_WORD:
+        if(metakom_process_bit(metakom, polarity, time, &high_time, &low_time)) {
+            if(low_time < (metakom->period_time / 2)) {
+                metakom->tmp_data = (metakom->tmp_data << 1) | 0b0;
+            } else {
+                metakom->tmp_data = (metakom->tmp_data << 1) | 0b1;
+            }
+            metakom->tmp_counter++;
+
+            if(metakom->tmp_counter == 3) {
+                if(metakom->tmp_data == 0b010) {
+                    metakom->tmp_counter = 0;
+                    metakom->tmp_data = 0;
+                    metakom->state = METAKOM_READ_WORD;
+                } else {
+                    metakom_reset(metakom);
+                }
+            }
+        }
+        break;
+    case METAKOM_READ_WORD:
+        if(metakom_process_bit(metakom, polarity, time, &high_time, &low_time)) {
+            if(low_time < (metakom->period_time / 2)) {
+                metakom->tmp_data = (metakom->tmp_data << 1) | 0b0;
+            } else {
+                metakom->tmp_data = (metakom->tmp_data << 1) | 0b1;
+            }
+            metakom->tmp_counter++;
+
+            if(metakom->tmp_counter == 8) {
+                if(metakom_parity_check(metakom->tmp_data)) {
+                    metakom->key_data = (metakom->key_data << 8) | metakom->tmp_data;
+                    metakom->key_data_index++;
+                    metakom->tmp_data = 0;
+                    metakom->tmp_counter = 0;
+
+                    if(metakom->key_data_index == 4) {
+                        // check for stop bit
+                        if(high_time > metakom->period_time) {
+                            metakom->state = METAKOM_READ_STOP_WORD;
+                        } else {
+                            metakom_reset(metakom);
+                        }
+                    }
+                } else {
+                    metakom_reset(metakom);
+                }
+            }
+        }
+        break;
+    case METAKOM_READ_STOP_WORD:
+        if(metakom_process_bit(metakom, polarity, time, &high_time, &low_time)) {
+            if(low_time < (metakom->period_time / 2)) {
+                metakom->tmp_data = (metakom->tmp_data << 1) | 0b0;
+            } else {
+                metakom->tmp_data = (metakom->tmp_data << 1) | 0b1;
+            }
+            metakom->tmp_counter++;
+
+            if(metakom->tmp_counter == 3) {
+                if(metakom->tmp_data == 0b010) {
+                    metakom->ready = true;
+                } else {
+                    metakom_reset(metakom);
+                }
+            }
+        }
+        break;
+    }
+}

+ 38 - 0
lib/one_wire/ibutton/pulse_protocols/protocol_metakom.h

@@ -0,0 +1,38 @@
+/**
+ * @file protocol_metakom.h
+ * 
+ * Metakom pulse format decoder
+ */
+
+#pragma once
+#include <stdint.h>
+#include "../../pulse_protocols/pulse_protocol.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct ProtocolMetakom ProtocolMetakom;
+
+/**
+ * Allocate decoder
+ * @return ProtocolMetakom* 
+ */
+ProtocolMetakom* protocol_metakom_alloc();
+
+/**
+ * Free decoder
+ * @param metakom 
+ */
+void protocol_metakom_free(ProtocolMetakom* metakom);
+
+/**
+ * Get protocol interface
+ * @param metakom 
+ * @return PulseProtocol* 
+ */
+PulseProtocol* protocol_metakom_get_protocol(ProtocolMetakom* metakom);
+
+#ifdef __cplusplus
+}
+#endif

+ 16 - 0
lib/one_wire/maxim_crc.c

@@ -0,0 +1,16 @@
+#include "maxim_crc.h"
+
+uint8_t maxim_crc8(const uint8_t* data, const uint8_t data_size, const uint8_t crc_init) {
+    uint8_t crc = crc_init;
+
+    for(uint8_t index = 0; index < data_size; ++index) {
+        uint8_t input_byte = data[index];
+        for(uint8_t bit_position = 0; bit_position < 8; ++bit_position) {
+            const uint8_t mix = (crc ^ input_byte) & (uint8_t)(0x01);
+            crc >>= 1;
+            if(mix != 0) crc ^= 0x8C;
+            input_byte >>= 1;
+        }
+    }
+    return crc;
+}

+ 14 - 0
lib/one_wire/maxim_crc.h

@@ -0,0 +1,14 @@
+#pragma once
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MAXIM_CRC8_INIT 0
+
+uint8_t maxim_crc8(const uint8_t* data, const uint8_t data_size, const uint8_t crc_init);
+
+#ifdef __cplusplus
+}
+#endif

+ 59 - 0
lib/one_wire/one_wire_device.c

@@ -0,0 +1,59 @@
+#include <stdlib.h>
+#include "maxim_crc.h"
+#include "one_wire_device.h"
+#include "one_wire_slave.h"
+#include "one_wire_slave_i.h"
+
+struct OneWireDevice {
+    uint8_t id_storage[8];
+    OneWireSlave* bus;
+};
+
+OneWireDevice* onewire_device_alloc(
+    uint8_t id_1,
+    uint8_t id_2,
+    uint8_t id_3,
+    uint8_t id_4,
+    uint8_t id_5,
+    uint8_t id_6,
+    uint8_t id_7,
+    uint8_t id_8) {
+    OneWireDevice* device = malloc(sizeof(OneWireDevice));
+    device->id_storage[0] = id_1;
+    device->id_storage[1] = id_2;
+    device->id_storage[2] = id_3;
+    device->id_storage[3] = id_4;
+    device->id_storage[4] = id_5;
+    device->id_storage[5] = id_6;
+    device->id_storage[6] = id_7;
+    device->id_storage[7] = id_8;
+    device->bus = NULL;
+
+    return device;
+}
+
+void onewire_device_free(OneWireDevice* device) {
+    if(device->bus != NULL) {
+        onewire_slave_detach(device->bus);
+    }
+
+    free(device);
+}
+
+void onewire_device_send_id(OneWireDevice* device) {
+    if(device->bus != NULL) {
+        onewire_slave_send(device->bus, device->id_storage, 8);
+    }
+}
+
+void onewire_device_attach(OneWireDevice* device, OneWireSlave* bus) {
+    device->bus = bus;
+}
+
+void onewire_device_detach(OneWireDevice* device) {
+    device->bus = NULL;
+}
+
+uint8_t* onewire_device_get_id_p(OneWireDevice* device) {
+    return device->id_storage;
+}

+ 74 - 0
lib/one_wire/one_wire_device.h

@@ -0,0 +1,74 @@
+/**
+ * @file one_wire_device.h
+ * 
+ * 1-Wire slave library, device interface. Currently it can only emulate ID.
+ */
+
+#pragma once
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct OneWireSlave OneWireSlave;
+typedef struct OneWireDevice OneWireDevice;
+
+/**
+ * Allocate onewire device with ID
+ * @param id_1 
+ * @param id_2 
+ * @param id_3 
+ * @param id_4 
+ * @param id_5 
+ * @param id_6 
+ * @param id_7 
+ * @param id_8 
+ * @return OneWireDevice* 
+ */
+OneWireDevice* onewire_device_alloc(
+    uint8_t id_1,
+    uint8_t id_2,
+    uint8_t id_3,
+    uint8_t id_4,
+    uint8_t id_5,
+    uint8_t id_6,
+    uint8_t id_7,
+    uint8_t id_8);
+
+/**
+ * Deallocate onewire device
+ * @param device 
+ */
+void onewire_device_free(OneWireDevice* device);
+
+/**
+ * Send ID report, called from onewire slave
+ * @param device 
+ */
+void onewire_device_send_id(OneWireDevice* device);
+
+/**
+ * Attach device to onewire slave bus
+ * @param device 
+ * @param bus 
+ */
+void onewire_device_attach(OneWireDevice* device, OneWireSlave* bus);
+
+/**
+ * Attach device from onewire slave bus
+ * @param device 
+ */
+void onewire_device_detach(OneWireDevice* device);
+
+/**
+ * Get pointer to device id array
+ * @param device 
+ * @return uint8_t* 
+ */
+uint8_t* onewire_device_get_id_p(OneWireDevice* device);
+
+#ifdef __cplusplus
+}
+#endif

+ 261 - 0
lib/one_wire/one_wire_host.c

@@ -0,0 +1,261 @@
+#include <furi.h>
+#include <furi_hal.h>
+#include "one_wire_host.h"
+#include "one_wire_host_timing.h"
+
+struct OneWireHost {
+    // global search state
+    unsigned char saved_rom[8];
+    uint8_t last_discrepancy;
+    uint8_t last_family_discrepancy;
+    bool last_device_flag;
+};
+
+OneWireHost* onewire_host_alloc() {
+    OneWireHost* host = malloc(sizeof(OneWireHost));
+    onewire_host_reset_search(host);
+    return host;
+}
+
+void onewire_host_free(OneWireHost* host) {
+    onewire_host_stop(host);
+    free(host);
+}
+
+bool onewire_host_reset(OneWireHost* host) {
+    uint8_t r;
+    uint8_t retries = 125;
+
+    // wait until the gpio is high
+    furi_hal_ibutton_pin_high();
+    do {
+        if(--retries == 0) return 0;
+        delay_us(2);
+    } while(!furi_hal_ibutton_pin_get_level());
+
+    // pre delay
+    delay_us(OWH_RESET_DELAY_PRE);
+
+    // drive low
+    furi_hal_ibutton_pin_low();
+    delay_us(OWH_RESET_DRIVE);
+
+    // release
+    furi_hal_ibutton_pin_high();
+    delay_us(OWH_RESET_RELEASE);
+
+    // read and post delay
+    r = !furi_hal_ibutton_pin_get_level();
+    delay_us(OWH_RESET_DELAY_POST);
+
+    return r;
+}
+
+bool onewire_host_read_bit(OneWireHost* host) {
+    bool result;
+
+    // drive low
+    furi_hal_ibutton_pin_low();
+    delay_us(OWH_READ_DRIVE);
+
+    // release
+    furi_hal_ibutton_pin_high();
+    delay_us(OWH_READ_RELEASE);
+
+    // read and post delay
+    result = furi_hal_ibutton_pin_get_level();
+    delay_us(OWH_READ_DELAY_POST);
+
+    return result;
+}
+
+uint8_t onewire_host_read(OneWireHost* host) {
+    uint8_t result = 0;
+
+    for(uint8_t bitMask = 0x01; bitMask; bitMask <<= 1) {
+        if(onewire_host_read_bit(host)) {
+            result |= bitMask;
+        }
+    }
+
+    return result;
+}
+
+void onewire_host_read_bytes(OneWireHost* host, uint8_t* buffer, uint16_t count) {
+    for(uint16_t i = 0; i < count; i++) {
+        buffer[i] = onewire_host_read(host);
+    }
+}
+
+void onewire_host_write_bit(OneWireHost* host, bool value) {
+    if(value) {
+        // drive low
+        furi_hal_ibutton_pin_low();
+        delay_us(OWH_WRITE_1_DRIVE);
+
+        // release
+        furi_hal_ibutton_pin_high();
+        delay_us(OWH_WRITE_1_RELEASE);
+    } else {
+        // drive low
+        furi_hal_ibutton_pin_low();
+        delay_us(OWH_WRITE_0_DRIVE);
+
+        // release
+        furi_hal_ibutton_pin_high();
+        delay_us(OWH_WRITE_0_RELEASE);
+    }
+}
+
+void onewire_host_write(OneWireHost* host, uint8_t value) {
+    uint8_t bitMask;
+
+    for(bitMask = 0x01; bitMask; bitMask <<= 1) {
+        onewire_host_write_bit(host, (bitMask & value) ? 1 : 0);
+    }
+}
+
+void onewire_host_skip(OneWireHost* host) {
+    onewire_host_write(host, 0xCC);
+}
+
+void onewire_host_start(OneWireHost* host) {
+    furi_hal_ibutton_start_drive();
+}
+
+void onewire_host_stop(OneWireHost* host) {
+    furi_hal_ibutton_stop();
+}
+
+void onewire_host_reset_search(OneWireHost* host) {
+    host->last_discrepancy = 0;
+    host->last_device_flag = false;
+    host->last_family_discrepancy = 0;
+    for(int i = 7;; i--) {
+        host->saved_rom[i] = 0;
+        if(i == 0) break;
+    }
+}
+
+void onewire_host_target_search(OneWireHost* host, uint8_t family_code) {
+    host->saved_rom[0] = family_code;
+    for(uint8_t i = 1; i < 8; i++) host->saved_rom[i] = 0;
+    host->last_discrepancy = 64;
+    host->last_family_discrepancy = 0;
+    host->last_device_flag = false;
+}
+
+uint8_t onewire_host_search(OneWireHost* host, uint8_t* newAddr, OneWireHostSearchMode mode) {
+    uint8_t id_bit_number;
+    uint8_t last_zero, rom_byte_number, search_result;
+    uint8_t id_bit, cmp_id_bit;
+
+    unsigned char rom_byte_mask, search_direction;
+
+    // initialize for search
+    id_bit_number = 1;
+    last_zero = 0;
+    rom_byte_number = 0;
+    rom_byte_mask = 1;
+    search_result = 0;
+
+    // if the last call was not the last one
+    if(!host->last_device_flag) {
+        // 1-Wire reset
+        if(!onewire_host_reset(host)) {
+            // reset the search
+            host->last_discrepancy = 0;
+            host->last_device_flag = false;
+            host->last_family_discrepancy = 0;
+            return false;
+        }
+
+        // issue the search command
+        switch(mode) {
+        case CONDITIONAL_SEARCH:
+            onewire_host_write(host, 0xEC);
+            break;
+        case NORMAL_SEARCH:
+            onewire_host_write(host, 0xF0);
+            break;
+        }
+
+        // loop to do the search
+        do {
+            // read a bit and its complement
+            id_bit = onewire_host_read_bit(host);
+            cmp_id_bit = onewire_host_read_bit(host);
+
+            // check for no devices on 1-wire
+            if((id_bit == 1) && (cmp_id_bit == 1))
+                break;
+            else {
+                // all devices coupled have 0 or 1
+                if(id_bit != cmp_id_bit)
+                    search_direction = id_bit; // bit write value for search
+                else {
+                    // if this discrepancy if before the Last Discrepancy
+                    // on a previous next then pick the same as last time
+                    if(id_bit_number < host->last_discrepancy)
+                        search_direction =
+                            ((host->saved_rom[rom_byte_number] & rom_byte_mask) > 0);
+                    else
+                        // if equal to last pick 1, if not then pick 0
+                        search_direction = (id_bit_number == host->last_discrepancy);
+
+                    // if 0 was picked then record its position in LastZero
+                    if(search_direction == 0) {
+                        last_zero = id_bit_number;
+
+                        // check for Last discrepancy in family
+                        if(last_zero < 9) host->last_family_discrepancy = last_zero;
+                    }
+                }
+
+                // set or clear the bit in the ROM byte rom_byte_number
+                // with mask rom_byte_mask
+                if(search_direction == 1)
+                    host->saved_rom[rom_byte_number] |= rom_byte_mask;
+                else
+                    host->saved_rom[rom_byte_number] &= ~rom_byte_mask;
+
+                // serial number search direction write bit
+                onewire_host_write_bit(host, search_direction);
+
+                // increment the byte counter id_bit_number
+                // and shift the mask rom_byte_mask
+                id_bit_number++;
+                rom_byte_mask <<= 1;
+
+                // if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask
+                if(rom_byte_mask == 0) {
+                    rom_byte_number++;
+                    rom_byte_mask = 1;
+                }
+            }
+        } while(rom_byte_number < 8); // loop until through all ROM bytes 0-7
+
+        // if the search was successful then
+        if(!(id_bit_number < 65)) {
+            // search successful so set last_Discrepancy, last_device_flag, search_result
+            host->last_discrepancy = last_zero;
+
+            // check for last device
+            if(host->last_discrepancy == 0) host->last_device_flag = true;
+
+            search_result = true;
+        }
+    }
+
+    // if no device found then reset counters so next 'search' will be like a first
+    if(!search_result || !host->saved_rom[0]) {
+        host->last_discrepancy = 0;
+        host->last_device_flag = false;
+        host->last_family_discrepancy = 0;
+        search_result = false;
+    } else {
+        for(int i = 0; i < 8; i++) newAddr[i] = host->saved_rom[i];
+    }
+
+    return search_result;
+}

+ 121 - 0
lib/one_wire/one_wire_host.h

@@ -0,0 +1,121 @@
+/**
+ * @file one_wire_host.h
+ * 
+ * 1-Wire host (master) library
+ */
+
+#pragma once
+#include <stdint.h>
+#include <stdbool.h>
+#include <furi_hal_gpio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    CONDITIONAL_SEARCH = 0, /**< Search for alarmed device */
+    NORMAL_SEARCH = 1, /**< Search all devices */
+} OneWireHostSearchMode;
+
+typedef struct OneWireHost OneWireHost;
+
+/**
+ * Allocate onewire host bus
+ * @param gpio 
+ * @return OneWireHost* 
+ */
+OneWireHost* onewire_host_alloc();
+
+/**
+ * Deallocate onewire host bus
+ * @param host 
+ */
+void onewire_host_free(OneWireHost* host);
+
+/**
+ * Reset bus
+ * @param host 
+ * @return bool 
+ */
+bool onewire_host_reset(OneWireHost* host);
+
+/**
+ * Read one bit
+ * @param host 
+ * @return bool 
+ */
+bool onewire_host_read_bit(OneWireHost* host);
+
+/**
+ * Read one byte
+ * @param host 
+ * @return uint8_t 
+ */
+uint8_t onewire_host_read(OneWireHost* host);
+
+/**
+ * Read many bytes
+ * @param host 
+ * @param buffer 
+ * @param count 
+ */
+void onewire_host_read_bytes(OneWireHost* host, uint8_t* buffer, uint16_t count);
+
+/**
+ * Write one bit
+ * @param host 
+ * @param value 
+ */
+void onewire_host_write_bit(OneWireHost* host, bool value);
+
+/**
+ * Write one byte
+ * @param host 
+ * @param value 
+ */
+void onewire_host_write(OneWireHost* host, uint8_t value);
+
+/**
+ * Skip ROM command
+ * @param host 
+ */
+void onewire_host_skip(OneWireHost* host);
+
+/**
+ * Start working with the bus
+ * @param host 
+ */
+void onewire_host_start(OneWireHost* host);
+
+/**
+ * Stop working with the bus
+ * @param host 
+ */
+void onewire_host_stop(OneWireHost* host);
+
+/**
+ * 
+ * @param host 
+ */
+void onewire_host_reset_search(OneWireHost* host);
+
+/**
+ * 
+ * @param host 
+ * @param family_code 
+ */
+void onewire_host_target_search(OneWireHost* host, uint8_t family_code);
+
+/**
+ * 
+ * @param host 
+ * @param newAddr 
+ * @param mode 
+ * @return uint8_t 
+ */
+uint8_t onewire_host_search(OneWireHost* host, uint8_t* newAddr, OneWireHostSearchMode mode);
+
+#ifdef __cplusplus
+}
+#endif

+ 30 - 0
lib/one_wire/one_wire_host_timing.h

@@ -0,0 +1,30 @@
+/**
+ * @file one_wire_host_timing.h
+ * 
+ * 1-Wire library, timing list
+ */
+
+#pragma once
+
+#define OWH_TIMING_A 9
+#define OWH_TIMING_B 64
+#define OWH_TIMING_C 64
+#define OWH_TIMING_D 14
+#define OWH_TIMING_E 9
+#define OWH_TIMING_F 55
+#define OWH_TIMING_G 0
+#define OWH_TIMING_H 480
+#define OWH_TIMING_I 70
+#define OWH_TIMING_J 410
+
+#define OWH_WRITE_1_DRIVE OWH_TIMING_A
+#define OWH_WRITE_1_RELEASE OWH_TIMING_B
+#define OWH_WRITE_0_DRIVE OWH_TIMING_C
+#define OWH_WRITE_0_RELEASE OWH_TIMING_D
+#define OWH_READ_DRIVE 3
+#define OWH_READ_RELEASE OWH_TIMING_E
+#define OWH_READ_DELAY_POST OWH_TIMING_F
+#define OWH_RESET_DELAY_PRE OWH_TIMING_G
+#define OWH_RESET_DRIVE OWH_TIMING_H
+#define OWH_RESET_RELEASE OWH_TIMING_I
+#define OWH_RESET_DELAY_POST OWH_TIMING_J

+ 317 - 0
lib/one_wire/one_wire_slave.c

@@ -0,0 +1,317 @@
+#include "one_wire_slave.h"
+#include "one_wire_slave_i.h"
+#include "one_wire_device.h"
+#include <furi.h>
+#include <furi_hal_delay.h>
+#include <furi_hal_ibutton.h>
+
+#define OWS_RESET_MIN 270
+#define OWS_RESET_MAX 960
+#define OWS_PRESENCE_TIMEOUT 20
+#define OWS_PRESENCE_MIN 100
+#define OWS_PRESENCE_MAX 480
+#define OWS_MSG_HIGH_TIMEOUT 15000
+#define OWS_SLOT_MAX 135
+#define OWS_READ_MIN 20
+#define OWS_READ_MAX 60
+#define OWS_WRITE_ZERO 30
+
+typedef enum {
+    NO_ERROR = 0,
+    VERY_LONG_RESET,
+    VERY_SHORT_RESET,
+    PRESENCE_LOW_ON_LINE,
+    AWAIT_TIMESLOT_TIMEOUT_HIGH,
+    INCORRECT_ONEWIRE_CMD,
+    FIRST_BIT_OF_BYTE_TIMEOUT,
+    RESET_IN_PROGRESS
+} OneWireSlaveError;
+
+struct OneWireSlave {
+    OneWireSlaveError error;
+    OneWireDevice* device;
+    OneWireSlaveResultCallback result_cb;
+    void* result_cb_ctx;
+};
+
+/*********************** PRIVATE ***********************/
+
+uint32_t onewire_slave_wait_while_gpio_is(OneWireSlave* bus, uint32_t time, const bool pin_value) {
+    uint32_t start = DWT->CYCCNT;
+    uint32_t time_ticks = time * instructions_per_us;
+    uint32_t time_captured;
+
+    do {
+        time_captured = DWT->CYCCNT;
+        if(furi_hal_ibutton_pin_get_level() != pin_value) {
+            uint32_t remaining_time = time_ticks - (time_captured - start);
+            remaining_time /= instructions_per_us;
+            return remaining_time;
+        }
+    } while((time_captured - start) < time_ticks);
+
+    return 0;
+}
+
+bool onewire_slave_show_presence(OneWireSlave* bus) {
+    // wait while master delay presence check
+    onewire_slave_wait_while_gpio_is(bus, OWS_PRESENCE_TIMEOUT, true);
+
+    // show presence
+    furi_hal_ibutton_pin_low();
+    delay_us(OWS_PRESENCE_MIN);
+    furi_hal_ibutton_pin_high();
+
+    // somebody also can show presence
+    const uint32_t wait_low_time = OWS_PRESENCE_MAX - OWS_PRESENCE_MIN;
+
+    // so we will wait
+    if(onewire_slave_wait_while_gpio_is(bus, wait_low_time, false) == 0) {
+        bus->error = PRESENCE_LOW_ON_LINE;
+        return false;
+    }
+
+    return true;
+}
+
+bool onewire_slave_receive_bit(OneWireSlave* bus) {
+    // wait while bus is low
+    uint32_t time = OWS_SLOT_MAX;
+    time = onewire_slave_wait_while_gpio_is(bus, time, false);
+    if(time == 0) {
+        bus->error = RESET_IN_PROGRESS;
+        return false;
+    }
+
+    // wait while bus is high
+    time = OWS_MSG_HIGH_TIMEOUT;
+    time = onewire_slave_wait_while_gpio_is(bus, time, true);
+    if(time == 0) {
+        bus->error = AWAIT_TIMESLOT_TIMEOUT_HIGH;
+        return false;
+    }
+
+    // wait a time of zero
+    time = OWS_READ_MIN;
+    time = onewire_slave_wait_while_gpio_is(bus, time, false);
+
+    return (time > 0);
+}
+
+bool onewire_slave_send_bit(OneWireSlave* bus, bool value) {
+    const bool write_zero = !value;
+
+    // wait while bus is low
+    uint32_t time = OWS_SLOT_MAX;
+    time = onewire_slave_wait_while_gpio_is(bus, time, false);
+    if(time == 0) {
+        bus->error = RESET_IN_PROGRESS;
+        return false;
+    }
+
+    // wait while bus is high
+    time = OWS_MSG_HIGH_TIMEOUT;
+    time = onewire_slave_wait_while_gpio_is(bus, time, true);
+    if(time == 0) {
+        bus->error = AWAIT_TIMESLOT_TIMEOUT_HIGH;
+        return false;
+    }
+
+    // choose write time
+    if(write_zero) {
+        furi_hal_ibutton_pin_low();
+        time = OWS_WRITE_ZERO;
+    } else {
+        time = OWS_READ_MAX;
+    }
+
+    // hold line for ZERO or ONE time
+    delay_us(time);
+    furi_hal_ibutton_pin_high();
+
+    return true;
+}
+
+void onewire_slave_cmd_search_rom(OneWireSlave* bus) {
+    const uint8_t key_bytes = 8;
+    uint8_t* key = onewire_device_get_id_p(bus->device);
+
+    for(uint8_t i = 0; i < key_bytes; i++) {
+        uint8_t key_byte = key[i];
+
+        for(uint8_t j = 0; j < 8; j++) {
+            bool bit = (key_byte >> j) & 0x01;
+
+            if(!onewire_slave_send_bit(bus, bit)) return;
+            if(!onewire_slave_send_bit(bus, !bit)) return;
+
+            onewire_slave_receive_bit(bus);
+            if(bus->error != NO_ERROR) return;
+        }
+    }
+}
+
+bool onewire_slave_receive_and_process_cmd(OneWireSlave* bus) {
+    uint8_t cmd;
+    onewire_slave_receive(bus, &cmd, 1);
+
+    if(bus->error == RESET_IN_PROGRESS) return true;
+    if(bus->error != NO_ERROR) return false;
+
+    switch(cmd) {
+    case 0xF0:
+        // SEARCH ROM
+        onewire_slave_cmd_search_rom(bus);
+        return true;
+
+    case 0x0F:
+    case 0x33:
+        // READ ROM
+        onewire_device_send_id(bus->device);
+        return true;
+
+    default: // Unknown command
+        bus->error = INCORRECT_ONEWIRE_CMD;
+    }
+
+    if(bus->error == RESET_IN_PROGRESS) return true;
+    return (bus->error == NO_ERROR);
+}
+
+bool onewire_slave_bus_start(OneWireSlave* bus) {
+    bool result = true;
+
+    if(bus->device == NULL) {
+        result = false;
+    } else {
+        FURI_CRITICAL_ENTER();
+        furi_hal_ibutton_start_drive_in_isr();
+        bus->error = NO_ERROR;
+
+        if(onewire_slave_show_presence(bus)) {
+            // TODO think about multiple command cycles
+            onewire_slave_receive_and_process_cmd(bus);
+            result = (bus->error == NO_ERROR || bus->error == INCORRECT_ONEWIRE_CMD);
+
+        } else {
+            result = false;
+        }
+
+        furi_hal_ibutton_start_interrupt_in_isr();
+        FURI_CRITICAL_EXIT();
+    }
+
+    return result;
+}
+
+static void exti_cb(void* context) {
+    OneWireSlave* bus = context;
+
+    volatile bool input_state = furi_hal_ibutton_pin_get_level();
+    static uint32_t pulse_start = 0;
+
+    if(input_state) {
+        uint32_t pulse_length = (DWT->CYCCNT - pulse_start) / instructions_per_us;
+        if(pulse_length >= OWS_RESET_MIN) {
+            if(pulse_length <= OWS_RESET_MAX) {
+                // reset cycle ok
+                bool result = onewire_slave_bus_start(bus);
+                if(result && bus->result_cb != NULL) {
+                    bus->result_cb(bus->result_cb_ctx);
+                }
+            } else {
+                bus->error = VERY_LONG_RESET;
+            }
+        } else {
+            bus->error = VERY_SHORT_RESET;
+        }
+    } else {
+        //FALL event
+        pulse_start = DWT->CYCCNT;
+    }
+};
+
+/*********************** PUBLIC ***********************/
+
+OneWireSlave* onewire_slave_alloc() {
+    OneWireSlave* bus = malloc(sizeof(OneWireSlave));
+    bus->error = NO_ERROR;
+    bus->device = NULL;
+    bus->result_cb = NULL;
+    bus->result_cb_ctx = NULL;
+    return bus;
+}
+
+void onewire_slave_free(OneWireSlave* bus) {
+    onewire_slave_stop(bus);
+    free(bus);
+}
+
+void onewire_slave_start(OneWireSlave* bus) {
+    furi_hal_ibutton_add_interrupt(exti_cb, bus);
+    furi_hal_ibutton_start_interrupt();
+}
+
+void onewire_slave_stop(OneWireSlave* bus) {
+    furi_hal_ibutton_stop();
+    furi_hal_ibutton_remove_interrupt();
+}
+
+void onewire_slave_attach(OneWireSlave* bus, OneWireDevice* device) {
+    bus->device = device;
+    onewire_device_attach(device, bus);
+}
+
+void onewire_slave_detach(OneWireSlave* bus) {
+    if(bus->device != NULL) {
+        onewire_device_detach(bus->device);
+    }
+    bus->device = NULL;
+}
+
+void onewire_slave_set_result_callback(
+    OneWireSlave* bus,
+    OneWireSlaveResultCallback result_cb,
+    void* context) {
+    bus->result_cb = result_cb;
+    bus->result_cb_ctx = context;
+}
+
+bool onewire_slave_send(OneWireSlave* bus, const uint8_t* address, const uint8_t data_length) {
+    uint8_t bytes_sent = 0;
+
+    furi_hal_ibutton_pin_high();
+
+    // bytes loop
+    for(; bytes_sent < data_length; ++bytes_sent) {
+        const uint8_t data_byte = address[bytes_sent];
+
+        // bit loop
+        for(uint8_t bit_mask = 0x01; bit_mask != 0; bit_mask <<= 1) {
+            if(!onewire_slave_send_bit(bus, bit_mask & data_byte)) {
+                // if we cannot send first bit
+                if((bit_mask == 0x01) && (bus->error == AWAIT_TIMESLOT_TIMEOUT_HIGH))
+                    bus->error = FIRST_BIT_OF_BYTE_TIMEOUT;
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+bool onewire_slave_receive(OneWireSlave* bus, uint8_t* data, const uint8_t data_length) {
+    uint8_t bytes_received = 0;
+
+    furi_hal_ibutton_pin_high();
+
+    for(; bytes_received < data_length; ++bytes_received) {
+        uint8_t value = 0;
+
+        for(uint8_t bit_mask = 0x01; bit_mask != 0; bit_mask <<= 1) {
+            if(onewire_slave_receive_bit(bus)) value |= bit_mask;
+        }
+
+        data[bytes_received] = value;
+    }
+    return (bytes_received != data_length);
+}

+ 71 - 0
lib/one_wire/one_wire_slave.h

@@ -0,0 +1,71 @@
+/**
+ * @file one_wire_slave.h
+ * 
+ * 1-Wire slave library. Currently it can only emulate ID.
+ */
+
+#pragma once
+#include <stdint.h>
+#include <stdbool.h>
+#include <furi_hal_gpio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct OneWireDevice OneWireDevice;
+typedef struct OneWireSlave OneWireSlave;
+typedef void (*OneWireSlaveResultCallback)(void* context);
+
+/**
+ * Allocate onewire slave
+ * @param pin 
+ * @return OneWireSlave* 
+ */
+OneWireSlave* onewire_slave_alloc();
+
+/**
+ * Free onewire slave
+ * @param bus 
+ */
+void onewire_slave_free(OneWireSlave* bus);
+
+/**
+ * Start working with the bus
+ * @param bus 
+ */
+void onewire_slave_start(OneWireSlave* bus);
+
+/**
+ * Stop working with the bus
+ * @param bus 
+ */
+void onewire_slave_stop(OneWireSlave* bus);
+
+/**
+ * Attach device for emulation
+ * @param bus 
+ * @param device 
+ */
+void onewire_slave_attach(OneWireSlave* bus, OneWireDevice* device);
+
+/**
+ * Detach device from bus
+ * @param bus 
+ */
+void onewire_slave_detach(OneWireSlave* bus);
+
+/**
+ * Set a callback to report emulation success
+ * @param bus 
+ * @param result_cb 
+ * @param context 
+ */
+void onewire_slave_set_result_callback(
+    OneWireSlave* bus,
+    OneWireSlaveResultCallback result_cb,
+    void* context);
+
+#ifdef __cplusplus
+}
+#endif

+ 38 - 0
lib/one_wire/one_wire_slave_i.h

@@ -0,0 +1,38 @@
+/**
+ * @file one_wire_slave_i.h
+ * 
+ * 1-Wire slave library, internal functions
+ */
+
+#pragma once
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct OneWireDevice OneWireDevice;
+typedef struct OneWireSlave OneWireSlave;
+
+/**
+ * Send data, called from emulated device
+ * @param bus 
+ * @param address 
+ * @param data_length 
+ * @return bool 
+ */
+bool onewire_slave_send(OneWireSlave* bus, const uint8_t* address, const uint8_t data_length);
+
+/**
+ * Receive data, called from emulated device
+ * @param bus 
+ * @param data 
+ * @param data_length 
+ * @return bool 
+ */
+bool onewire_slave_receive(OneWireSlave* bus, uint8_t* data, const uint8_t data_length);
+
+#ifdef __cplusplus
+}
+#endif

+ 66 - 0
lib/one_wire/pulse_protocols/pulse_decoder.c

@@ -0,0 +1,66 @@
+#include <stdlib.h>
+#include "pulse_decoder.h"
+#include <string.h>
+#include <furi/check.h>
+
+#define MAX_PROTOCOL 5
+
+struct PulseDecoder {
+    PulseProtocol* protocols[MAX_PROTOCOL];
+};
+
+PulseDecoder* pulse_decoder_alloc() {
+    PulseDecoder* decoder = malloc(sizeof(PulseDecoder));
+    memset(decoder, 0, sizeof(PulseDecoder));
+    return decoder;
+}
+
+void pulse_decoder_free(PulseDecoder* reader) {
+    furi_assert(reader);
+    free(reader);
+}
+
+void pulse_decoder_add_protocol(PulseDecoder* reader, PulseProtocol* protocol, int32_t index) {
+    furi_check(index < MAX_PROTOCOL);
+    furi_check(reader->protocols[index] == NULL);
+    reader->protocols[index] = protocol;
+}
+
+void pulse_decoder_process_pulse(PulseDecoder* reader, bool polarity, uint32_t length) {
+    furi_assert(reader);
+    for(size_t index = 0; index < MAX_PROTOCOL; index++) {
+        if(reader->protocols[index] != NULL) {
+            pulse_protocol_process_pulse(reader->protocols[index], polarity, length);
+        }
+    }
+}
+
+int32_t pulse_decoder_get_decoded_index(PulseDecoder* reader) {
+    furi_assert(reader);
+    int32_t decoded = -1;
+    for(size_t index = 0; index < MAX_PROTOCOL; index++) {
+        if(reader->protocols[index] != NULL) {
+            if(pulse_protocol_decoded(reader->protocols[index])) {
+                decoded = index;
+                break;
+            }
+        }
+    }
+
+    return decoded;
+}
+
+void pulse_decoder_reset(PulseDecoder* reader) {
+    furi_assert(reader);
+    for(size_t index = 0; index < MAX_PROTOCOL; index++) {
+        if(reader->protocols[index] != NULL) {
+            pulse_protocol_reset(reader->protocols[index]);
+        }
+    }
+}
+
+void pulse_decoder_get_data(PulseDecoder* reader, int32_t index, uint8_t* data, size_t length) {
+    furi_assert(reader);
+    furi_check(reader->protocols[index] != NULL);
+    pulse_protocol_get_data(reader->protocols[index], data, length);
+}

+ 70 - 0
lib/one_wire/pulse_protocols/pulse_decoder.h

@@ -0,0 +1,70 @@
+/**
+ * @file pulse_decoder.h
+ * 
+ * Generic pulse protocol decoder library
+ */
+
+#pragma once
+#include <stdint.h>
+#include <stdbool.h>
+#include "pulse_protocol.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct PulseDecoder PulseDecoder;
+
+/**
+ * Allocate decoder
+ * @return PulseDecoder* 
+ */
+PulseDecoder* pulse_decoder_alloc();
+
+/**
+ * Deallocate decoder
+ * @param decoder 
+ */
+void pulse_decoder_free(PulseDecoder* decoder);
+
+/**
+ * Add protocol to decoder
+ * @param decoder 
+ * @param protocol protocol implementation
+ * @param index protocol index, should not be repeated
+ */
+void pulse_decoder_add_protocol(PulseDecoder* decoder, PulseProtocol* protocol, int32_t index);
+
+/**
+ * Push and process pulse with decoder
+ * @param decoder 
+ * @param polarity 
+ * @param length 
+ */
+void pulse_decoder_process_pulse(PulseDecoder* decoder, bool polarity, uint32_t length);
+
+/**
+ * Get indec of decoded protocol
+ * @param decoder 
+ * @return int32_t, -1 if nothing decoded, or index of decoded protocol 
+ */
+int32_t pulse_decoder_get_decoded_index(PulseDecoder* decoder);
+
+/**
+ * Reset all protocols in decoder
+ * @param decoder 
+ */
+void pulse_decoder_reset(PulseDecoder* decoder);
+
+/**
+ * Get decoded data from protocol
+ * @param decoder 
+ * @param index 
+ * @param data 
+ * @param length 
+ */
+void pulse_decoder_get_data(PulseDecoder* decoder, int32_t index, uint8_t* data, size_t length);
+
+#ifdef __cplusplus
+}
+#endif

+ 55 - 0
lib/one_wire/pulse_protocols/pulse_glue.c

@@ -0,0 +1,55 @@
+#include "pulse_glue.h"
+
+struct PulseGlue {
+    int32_t hi_period;
+    int32_t low_period;
+    int32_t next_hi_period;
+};
+
+PulseGlue* pulse_glue_alloc() {
+    PulseGlue* pulse_glue = malloc(sizeof(PulseGlue));
+    pulse_glue_reset(pulse_glue);
+    return pulse_glue;
+}
+
+void pulse_glue_free(PulseGlue* pulse_glue) {
+    free(pulse_glue);
+}
+
+void pulse_glue_reset(PulseGlue* pulse_glue) {
+    pulse_glue->hi_period = 0;
+    pulse_glue->low_period = 0;
+    pulse_glue->next_hi_period = 0;
+}
+
+bool pulse_glue_push(PulseGlue* pulse_glue, bool polarity, uint32_t length) {
+    bool pop_ready = false;
+    if(polarity) {
+        if(pulse_glue->low_period == 0) {
+            // stage 1, accumulate hi period
+            pulse_glue->hi_period += length;
+        } else {
+            // stage 3, accumulate next hi period and be ready for pulse_glue_pop
+            pulse_glue->next_hi_period = length;
+
+            // data is ready
+            pop_ready = true;
+        }
+    } else {
+        if(pulse_glue->hi_period != 0) {
+            // stage 2, accumulate low period
+            pulse_glue->low_period += length;
+        }
+    }
+
+    return pop_ready;
+}
+
+void pulse_glue_pop(PulseGlue* pulse_glue, uint32_t* length, uint32_t* period) {
+    *length = pulse_glue->hi_period + pulse_glue->low_period;
+    *period = pulse_glue->hi_period;
+
+    pulse_glue->hi_period = pulse_glue->next_hi_period;
+    pulse_glue->low_period = 0;
+    pulse_glue->next_hi_period = 0;
+}

+ 26 - 0
lib/one_wire/pulse_protocols/pulse_glue.h

@@ -0,0 +1,26 @@
+/**
+ * @file pulse_glue.h
+ * 
+ * Simple tool to glue separated pulses to corret 
+ */
+#pragma once
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct PulseGlue PulseGlue;
+
+PulseGlue* pulse_glue_alloc();
+void pulse_glue_free(PulseGlue* pulse_glue);
+void pulse_glue_reset(PulseGlue* pulse_glue);
+
+bool pulse_glue_push(PulseGlue* pulse_glue, bool polarity, uint32_t length);
+void pulse_glue_pop(PulseGlue* pulse_glue, uint32_t* length, uint32_t* period);
+
+#ifdef __cplusplus
+}
+#endif

+ 67 - 0
lib/one_wire/pulse_protocols/pulse_protocol.c

@@ -0,0 +1,67 @@
+#include "pulse_protocol.h"
+#include <stdlib.h>
+#include <string.h>
+
+struct PulseProtocol {
+    void* context;
+    PulseProtocolPulseCallback pulse_cb;
+    PulseProtocolResetCallback reset_cb;
+    PulseProtocolGetDataCallback get_data_cb;
+    PulseProtocolDecodedCallback decoded_cb;
+};
+
+PulseProtocol* pulse_protocol_alloc() {
+    PulseProtocol* protocol = malloc(sizeof(PulseProtocol));
+    memset(protocol, 0, sizeof(PulseProtocol));
+    return protocol;
+}
+
+void pulse_protocol_set_context(PulseProtocol* protocol, void* context) {
+    protocol->context = context;
+}
+
+void pulse_protocol_set_pulse_cb(PulseProtocol* protocol, PulseProtocolPulseCallback callback) {
+    protocol->pulse_cb = callback;
+}
+
+void pulse_protocol_set_reset_cb(PulseProtocol* protocol, PulseProtocolResetCallback callback) {
+    protocol->reset_cb = callback;
+}
+
+void pulse_protocol_set_get_data_cb(PulseProtocol* protocol, PulseProtocolGetDataCallback callback) {
+    protocol->get_data_cb = callback;
+}
+
+void pulse_protocol_set_decoded_cb(PulseProtocol* protocol, PulseProtocolDecodedCallback callback) {
+    protocol->decoded_cb = callback;
+}
+
+void pulse_protocol_free(PulseProtocol* protocol) {
+    free(protocol);
+}
+
+void pulse_protocol_process_pulse(PulseProtocol* protocol, bool polarity, uint32_t length) {
+    if(protocol->pulse_cb != NULL) {
+        protocol->pulse_cb(protocol->context, polarity, length);
+    }
+}
+
+void pulse_protocol_reset(PulseProtocol* protocol) {
+    if(protocol->reset_cb != NULL) {
+        protocol->reset_cb(protocol->context);
+    }
+}
+
+bool pulse_protocol_decoded(PulseProtocol* protocol) {
+    bool result = false;
+    if(protocol->decoded_cb != NULL) {
+        result = protocol->decoded_cb(protocol->context);
+    }
+    return result;
+}
+
+void pulse_protocol_get_data(PulseProtocol* protocol, uint8_t* data, size_t length) {
+    if(protocol->get_data_cb != NULL) {
+        protocol->get_data_cb(protocol->context, data, length);
+    }
+}

+ 122 - 0
lib/one_wire/pulse_protocols/pulse_protocol.h

@@ -0,0 +1,122 @@
+/**
+ * @file pulse_protocol.h
+ * 
+ * Generic pulse protocol decoder library, protocol interface
+ */
+
+#pragma once
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Anonymous PulseProtocol struct
+ */
+typedef struct PulseProtocol PulseProtocol;
+
+/**
+ * Process pulse callback
+ */
+typedef void (*PulseProtocolPulseCallback)(void* context, bool polarity, uint32_t length);
+
+/**
+ * Reset protocol callback
+ */
+typedef void (*PulseProtocolResetCallback)(void* context);
+
+/**
+ * Get decoded data callback
+ */
+typedef void (*PulseProtocolGetDataCallback)(void* context, uint8_t* data, size_t length);
+
+/**
+ * Is protocol decoded callback
+ */
+typedef bool (*PulseProtocolDecodedCallback)(void* context);
+
+/**
+ * Allocate protocol
+ * @return PulseProtocol* 
+ */
+PulseProtocol* pulse_protocol_alloc();
+
+/**
+ * Deallocate protocol
+ * @param protocol 
+ */
+void pulse_protocol_free(PulseProtocol* protocol);
+
+/**
+ * Set context for callbacks
+ * @param protocol 
+ * @param context 
+ */
+void pulse_protocol_set_context(PulseProtocol* protocol, void* context);
+
+/**
+ * Set "Process pulse" callback. Called from the decoder when a new pulse is received.
+ * @param protocol 
+ * @param callback 
+ */
+void pulse_protocol_set_pulse_cb(PulseProtocol* protocol, PulseProtocolPulseCallback callback);
+
+/**
+ * Set "Reset protocol" callback. Called from the decoder when the decoder is reset.
+ * @param protocol 
+ * @param callback 
+ */
+void pulse_protocol_set_reset_cb(PulseProtocol* protocol, PulseProtocolResetCallback callback);
+
+/**
+ * Set "Get decoded data" callback. Called from the decoder when the decoder wants to get decoded data.
+ * @param protocol 
+ * @param callback 
+ */
+void pulse_protocol_set_get_data_cb(PulseProtocol* protocol, PulseProtocolGetDataCallback callback);
+
+/**
+ * Set "Is protocol decoded" callback. Called from the decoder when the decoder wants to know if a protocol has been decoded.
+ * @param protocol 
+ * @param callback 
+ */
+void pulse_protocol_set_decoded_cb(PulseProtocol* protocol, PulseProtocolDecodedCallback callback);
+
+/**
+ * Part of decoder interface.
+ * @param protocol 
+ * @param polarity 
+ * @param length 
+ */
+void pulse_protocol_process_pulse(PulseProtocol* protocol, bool polarity, uint32_t length);
+
+/**
+ * Part of decoder interface.
+ * @param protocol 
+ * @return true 
+ * @return false 
+ */
+bool pulse_protocol_decoded(PulseProtocol* protocol);
+
+/**
+ * Part of decoder interface.
+ * @param protocol 
+ * @return true 
+ * @return false 
+ */
+void pulse_protocol_get_data(PulseProtocol* protocol, uint8_t* data, size_t length);
+
+/**
+ * Part of decoder interface.
+ * @param protocol 
+ * @return true 
+ * @return false 
+ */
+void pulse_protocol_reset(PulseProtocol* protocol);
+
+#ifdef __cplusplus
+}
+#endif

部分文件因为文件数量过多而无法显示