SG 4 лет назад
Родитель
Сommit
c3350990c2

+ 176 - 0
applications/accessor/accessor-app.cpp

@@ -0,0 +1,176 @@
+#include "accessor-app.h"
+#include <furi.h>
+#include <api-hal.h>
+#include <stdarg.h>
+
+void AccessorApp::run(void) {
+    AccessorEvent event;
+    bool consumed;
+    bool exit = false;
+
+    notify_init();
+
+    wiegand.begin();
+    onewire_master.start();
+
+    scenes[current_scene]->on_enter(this);
+
+    while(!exit) {
+        view.receive_event(&event);
+
+        consumed = scenes[current_scene]->on_event(this, &event);
+
+        if(!consumed) {
+            if(event.type == AccessorEvent::Type::Back) {
+                exit = switch_to_previous_scene();
+            }
+        }
+    };
+
+    scenes[current_scene]->on_exit(this);
+}
+
+AccessorApp::AccessorApp()
+    : onewire_master{&ibutton_gpio} {
+    api_hal_power_insomnia_enter();
+}
+
+AccessorApp::~AccessorApp() {
+    api_hal_power_insomnia_exit();
+}
+
+AccessorAppViewManager* AccessorApp::get_view_manager() {
+    return &view;
+}
+
+void AccessorApp::switch_to_next_scene(Scene next_scene) {
+    previous_scenes_list.push_front(current_scene);
+
+    if(next_scene != Scene::Exit) {
+        scenes[current_scene]->on_exit(this);
+        current_scene = next_scene;
+        scenes[current_scene]->on_enter(this);
+    }
+}
+
+void AccessorApp::search_and_switch_to_previous_scene(std::initializer_list<Scene> scenes_list) {
+    Scene previous_scene = Scene::Start;
+    bool scene_found = false;
+
+    while(!scene_found) {
+        previous_scene = get_previous_scene();
+        for(Scene element : scenes_list) {
+            if(previous_scene == element || previous_scene == Scene::Start) {
+                scene_found = true;
+                break;
+            }
+        }
+    }
+
+    scenes[current_scene]->on_exit(this);
+    current_scene = previous_scene;
+    scenes[current_scene]->on_enter(this);
+}
+
+bool AccessorApp::switch_to_previous_scene(uint8_t count) {
+    Scene previous_scene = Scene::Start;
+
+    for(uint8_t i = 0; i < count; i++) {
+        previous_scene = get_previous_scene();
+        if(previous_scene == Scene::Exit) break;
+    }
+
+    if(previous_scene == Scene::Exit) {
+        return true;
+    } else {
+        scenes[current_scene]->on_exit(this);
+        current_scene = previous_scene;
+        scenes[current_scene]->on_enter(this);
+        return false;
+    }
+}
+
+AccessorApp::Scene AccessorApp::get_previous_scene() {
+    Scene scene = previous_scenes_list.front();
+    previous_scenes_list.pop_front();
+    return scene;
+}
+
+/***************************** NOTIFY *******************************/
+
+void AccessorApp::notify_init() {
+    // TODO open record
+    const GpioPin* vibro_record = &vibro_gpio;
+    gpio_init(vibro_record, GpioModeOutputPushPull);
+    gpio_write(vibro_record, false);
+
+    GPIO_InitTypeDef GPIO_InitStruct = {0};
+
+    GPIO_InitStruct.Pin = PB3_Pin;
+    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+    GPIO_InitStruct.Pull = GPIO_NOPULL;
+    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
+    GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
+    HAL_GPIO_Init(PB3_GPIO_Port, &GPIO_InitStruct);
+}
+
+void AccessorApp::notify_green_blink() {
+    api_hal_light_set(LightGreen, 0xFF);
+    delay(10);
+    api_hal_light_set(LightGreen, 0x00);
+}
+
+void AccessorApp::notify_green_on() {
+    api_hal_light_set(LightGreen, 0xFF);
+}
+
+void AccessorApp::notify_green_off() {
+    api_hal_light_set(LightGreen, 0x00);
+}
+
+void AccessorApp::notify_success() {
+    api_hal_light_set(LightBacklight, 0xFF);
+
+    hal_pwm_set(0.5, 1760 / 2, &htim2, TIM_CHANNEL_2);
+    notify_green_on();
+    delay(100);
+    hal_pwm_stop(&htim2, TIM_CHANNEL_2);
+    notify_green_off();
+
+    delay(100);
+
+    hal_pwm_set(0.5, 1760, &htim2, TIM_CHANNEL_2);
+    notify_green_on();
+    delay(100);
+    hal_pwm_stop(&htim2, TIM_CHANNEL_2);
+    notify_green_off();
+}
+
+/*************************** TEXT STORE *****************************/
+
+char* AccessorApp::get_text_store() {
+    return text_store;
+}
+
+uint8_t AccessorApp::get_text_store_size() {
+    return text_store_size;
+}
+
+void AccessorApp::set_text_store(const char* text...) {
+    va_list args;
+    va_start(args, text);
+
+    vsnprintf(text_store, text_store_size, text, args);
+
+    va_end(args);
+}
+
+/*************************** APP RESOURCES *****************************/
+
+WIEGAND* AccessorApp::get_wiegand() {
+    return &wiegand;
+}
+
+OneWireMaster* AccessorApp::get_one_wire() {
+    return &onewire_master;
+}

+ 58 - 0
applications/accessor/accessor-app.h

@@ -0,0 +1,58 @@
+#pragma once
+#include <map>
+#include <list>
+#include "accessor-view-manager.h"
+
+#include "scene/accessor-scene-start.h"
+
+#include "helpers/wiegand.h"
+
+#include <one_wire_master.h>
+
+class AccessorApp {
+public:
+    void run(void);
+
+    AccessorApp();
+    ~AccessorApp();
+
+    enum class Scene : uint8_t {
+        Exit,
+        Start,
+    };
+
+    AccessorAppViewManager* get_view_manager();
+    void switch_to_next_scene(Scene index);
+    void search_and_switch_to_previous_scene(std::initializer_list<Scene> scenes_list);
+    bool switch_to_previous_scene(uint8_t count = 1);
+    Scene get_previous_scene();
+
+    void notify_init();
+    void notify_green_blink();
+    void notify_green_on();
+    void notify_green_off();
+
+    void notify_success();
+
+    char* get_text_store();
+    uint8_t get_text_store_size();
+    void set_text_store(const char* text...);
+
+    WIEGAND* get_wiegand();
+    OneWireMaster* get_one_wire();
+
+private:
+    std::list<Scene> previous_scenes_list = {Scene::Exit};
+    Scene current_scene = Scene::Start;
+    AccessorAppViewManager view;
+
+    std::map<Scene, AccessorScene*> scenes = {
+        {Scene::Start, new AccessorSceneStart()},
+    };
+
+    static const uint8_t text_store_size = 128;
+    char text_store[text_store_size + 1];
+
+    WIEGAND wiegand;
+    OneWireMaster onewire_master;
+};

+ 19 - 0
applications/accessor/accessor-event.h

@@ -0,0 +1,19 @@
+#pragma once
+#include <stdint.h>
+
+class AccessorEvent {
+public:
+    // events enum
+    enum class Type : uint8_t {
+        Tick,
+        Back,
+    };
+
+    // payload
+    union {
+        uint32_t menu_index;
+    } payload;
+
+    // event type
+    Type type;
+};

+ 79 - 0
applications/accessor/accessor-view-manager.cpp

@@ -0,0 +1,79 @@
+#include "accessor-view-manager.h"
+#include "accessor-event.h"
+#include <callback-connector.h>
+
+AccessorAppViewManager::AccessorAppViewManager() {
+    event_queue = osMessageQueueNew(10, sizeof(AccessorEvent), NULL);
+
+    view_dispatcher = view_dispatcher_alloc();
+    auto callback = cbc::obtain_connector(this, &AccessorAppViewManager::previous_view_callback);
+
+    // allocate views
+    submenu = submenu_alloc();
+    add_view(ViewType::Submenu, submenu_get_view(submenu));
+
+    popup = popup_alloc();
+    add_view(ViewType::Popup, popup_get_view(popup));
+
+    gui = static_cast<Gui*>(furi_record_open("gui"));
+    view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen);
+
+    // set previous view callback for all views
+    view_set_previous_callback(submenu_get_view(submenu), callback);
+    view_set_previous_callback(popup_get_view(popup), callback);
+}
+
+AccessorAppViewManager::~AccessorAppViewManager() {
+    // remove views
+    view_dispatcher_remove_view(
+        view_dispatcher, static_cast<uint32_t>(AccessorAppViewManager::ViewType::Submenu));
+    view_dispatcher_remove_view(
+        view_dispatcher, static_cast<uint32_t>(AccessorAppViewManager::ViewType::Popup));
+
+    // free view modules
+    submenu_free(submenu);
+    popup_free(popup);
+
+    // free dispatcher
+    view_dispatcher_free(view_dispatcher);
+
+    // free event queue
+    osMessageQueueDelete(event_queue);
+}
+
+void AccessorAppViewManager::switch_to(ViewType type) {
+    view_dispatcher_switch_to_view(view_dispatcher, static_cast<uint32_t>(type));
+}
+
+Submenu* AccessorAppViewManager::get_submenu() {
+    return submenu;
+}
+
+Popup* AccessorAppViewManager::get_popup() {
+    return popup;
+}
+
+void AccessorAppViewManager::receive_event(AccessorEvent* event) {
+    if(osMessageQueueGet(event_queue, event, NULL, 100) != osOK) {
+        event->type = AccessorEvent::Type::Tick;
+    }
+}
+
+void AccessorAppViewManager::send_event(AccessorEvent* event) {
+    osStatus_t result = osMessageQueuePut(event_queue, event, 0, 0);
+    furi_check(result == osOK);
+}
+
+uint32_t AccessorAppViewManager::previous_view_callback(void* context) {
+    if(event_queue != NULL) {
+        AccessorEvent event;
+        event.type = AccessorEvent::Type::Back;
+        send_event(&event);
+    }
+
+    return VIEW_IGNORE;
+}
+
+void AccessorAppViewManager::add_view(ViewType view_type, View* view) {
+    view_dispatcher_add_view(view_dispatcher, static_cast<uint32_t>(view_type), view);
+}

+ 39 - 0
applications/accessor/accessor-view-manager.h

@@ -0,0 +1,39 @@
+#pragma once
+#include <furi.h>
+#include <gui/view_dispatcher.h>
+#include <gui/modules/submenu.h>
+#include <gui/modules/popup.h>
+#include "accessor-event.h"
+
+class AccessorAppViewManager {
+public:
+    enum class ViewType : uint8_t {
+        Submenu,
+        Popup,
+        Tune,
+    };
+
+    osMessageQueueId_t event_queue;
+
+    AccessorAppViewManager();
+    ~AccessorAppViewManager();
+
+    void switch_to(ViewType type);
+
+    void receive_event(AccessorEvent* event);
+    void send_event(AccessorEvent* event);
+
+    Submenu* get_submenu();
+    Popup* get_popup();
+
+private:
+    ViewDispatcher* view_dispatcher;
+    Gui* gui;
+
+    uint32_t previous_view_callback(void* context);
+    void add_view(ViewType view_type, View* view);
+
+    // view elements
+    Submenu* submenu;
+    Popup* popup;
+};

+ 10 - 0
applications/accessor/accessor.cpp

@@ -0,0 +1,10 @@
+#include "accessor-app.h"
+
+// app enter function
+extern "C" int32_t app_accessor(void* p) {
+    AccessorApp* app = new AccessorApp();
+    app->run();
+    delete app;
+
+    return 255;
+}

+ 221 - 0
applications/accessor/helpers/wiegand.cpp

@@ -0,0 +1,221 @@
+#include "wiegand.h"
+#include <furi.h>
+#include <api-hal.h>
+
+volatile unsigned long WIEGAND::_cardTempHigh = 0;
+volatile unsigned long WIEGAND::_cardTemp = 0;
+volatile unsigned long WIEGAND::_lastWiegand = 0;
+unsigned long WIEGAND::_code = 0;
+unsigned long WIEGAND::_codeHigh = 0;
+volatile int WIEGAND::_bitCount = 0;
+int WIEGAND::_wiegandType = 0;
+
+constexpr uint32_t clocks_in_ms = 64 * 1000;
+
+WIEGAND::WIEGAND() {
+}
+
+unsigned long WIEGAND::getCode() {
+    return _code;
+}
+
+unsigned long WIEGAND::getCodeHigh() {
+    return _codeHigh;
+}
+
+int WIEGAND::getWiegandType() {
+    return _wiegandType;
+}
+
+bool WIEGAND::available() {
+    bool ret;
+    __disable_irq();
+    ret = DoWiegandConversion();
+    __enable_irq();
+    return ret;
+}
+
+void input_isr(void* _pin, void* _ctx) {
+    // interrupt manager get us pin constant, so...
+    uint32_t pin = (uint32_t)_pin;
+    WIEGAND* _this = static_cast<WIEGAND*>(_ctx);
+
+    if(pin == ext_pa6_gpio.pin) {
+        _this->ReadD0();
+    }
+
+    if(pin == ext_pa7_gpio.pin) {
+        _this->ReadD1();
+    }
+}
+
+void WIEGAND::begin() {
+    _lastWiegand = 0;
+    _cardTempHigh = 0;
+    _cardTemp = 0;
+    _code = 0;
+    _wiegandType = 0;
+    _bitCount = 0;
+
+    const GpioPin* pinD0 = &ext_pa6_gpio;
+    const GpioPin* pinD1 = &ext_pa7_gpio;
+
+    gpio_init(pinD0, GpioModeInterruptFall); // Set D0 pin as input
+    gpio_init(pinD1, GpioModeInterruptFall); // Set D1 pin as input
+
+    api_interrupt_add(
+        input_isr, InterruptTypeExternalInterrupt, this); // Hardware interrupt - high to low pulse
+}
+
+void WIEGAND::ReadD0() {
+    _bitCount++; // Increament bit count for Interrupt connected to D0
+    if(_bitCount > 31) // If bit count more than 31, process high bits
+    {
+        _cardTempHigh |= ((0x80000000 & _cardTemp) >> 31); //	shift value to high bits
+        _cardTempHigh <<= 1;
+        _cardTemp <<= 1;
+    } else {
+        _cardTemp <<= 1; // D0 represent binary 0, so just left shift card data
+    }
+    _lastWiegand = DWT->CYCCNT; // Keep track of last wiegand bit received
+}
+
+void WIEGAND::ReadD1() {
+    _bitCount++; // Increment bit count for Interrupt connected to D1
+    if(_bitCount > 31) // If bit count more than 31, process high bits
+    {
+        _cardTempHigh |= ((0x80000000 & _cardTemp) >> 31); // shift value to high bits
+        _cardTempHigh <<= 1;
+        _cardTemp |= 1;
+        _cardTemp <<= 1;
+    } else {
+        _cardTemp |= 1; // D1 represent binary 1, so OR card data with 1 then
+        _cardTemp <<= 1; // left shift card data
+    }
+    _lastWiegand = DWT->CYCCNT; // Keep track of last wiegand bit received
+}
+
+unsigned long WIEGAND::GetCardId(
+    volatile unsigned long* codehigh,
+    volatile unsigned long* codelow,
+    char bitlength) {
+    if(bitlength == 26) // EM tag
+        return (*codelow & 0x1FFFFFE) >> 1;
+
+    if(bitlength == 24) return (*codelow & 0x7FFFFE) >> 1;
+
+    if(bitlength == 34) // Mifare
+    {
+        *codehigh = *codehigh & 0x03; // only need the 2 LSB of the codehigh
+        *codehigh <<= 30; // shift 2 LSB to MSB
+        *codelow >>= 1;
+        return *codehigh | *codelow;
+    }
+
+    if(bitlength == 32) {
+        return (*codelow & 0x7FFFFFFE) >> 1;
+    }
+
+    return *codelow; // EM tag or Mifare without parity bits
+}
+
+char translateEnterEscapeKeyPress(char originalKeyPress) {
+    switch(originalKeyPress) {
+    case 0x0b: // 11 or * key
+        return 0x0d; // 13 or ASCII ENTER
+
+    case 0x0a: // 10 or # key
+        return 0x1b; // 27 or ASCII ESCAPE
+
+    default:
+        return originalKeyPress;
+    }
+}
+
+bool WIEGAND::DoWiegandConversion() {
+    unsigned long cardID;
+    unsigned long sysTick = DWT->CYCCNT;
+
+    if((sysTick - _lastWiegand) >
+       (25 * clocks_in_ms)) // if no more signal coming through after 25ms
+    {
+        if((_bitCount == 24) || (_bitCount == 26) || (_bitCount == 32) || (_bitCount == 34) ||
+           (_bitCount == 37) || (_bitCount == 40) || (_bitCount == 8) ||
+           (_bitCount ==
+            4)) // bitCount for keypress=4 or 8, Wiegand 26=24 or 26, Wiegand 34=32 or 34
+        {
+            _codeHigh = 0;
+            // shift right 1 bit to get back the real value - interrupt done 1 left shift in advance
+            _cardTemp >>= 1;
+            // bit count more than 32 bits, shift high bits right to make adjustment
+            if(_bitCount > 32) _cardTempHigh >>= 1;
+
+            if(_bitCount == 8) // keypress wiegand with integrity
+            {
+                // 8-bit Wiegand keyboard data, high nibble is the "NOT" of low nibble
+                // eg if key 1 pressed, data=E1 in binary 11100001 , high nibble=1110 , low nibble = 0001
+                char highNibble = (_cardTemp & 0xf0) >> 4;
+                char lowNibble = (_cardTemp & 0x0f);
+                _wiegandType = _bitCount;
+                _bitCount = 0;
+                _cardTemp = 0;
+                _cardTempHigh = 0;
+
+                if(lowNibble ==
+                   (~highNibble & 0x0f)) // check if low nibble matches the "NOT" of high nibble.
+                {
+                    _code = (int)translateEnterEscapeKeyPress(lowNibble);
+                    return true;
+                } else {
+                    _lastWiegand = sysTick;
+                    _bitCount = 0;
+                    _cardTemp = 0;
+                    _cardTempHigh = 0;
+                    return false;
+                }
+
+                // TODO: Handle validation failure case!
+            } else if(4 == _bitCount) {
+                // 4-bit Wiegand codes have no data integrity check so we just
+                // read the LOW nibble.
+                _code = (int)translateEnterEscapeKeyPress(_cardTemp & 0x0000000F);
+
+                _wiegandType = _bitCount;
+                _bitCount = 0;
+                _cardTemp = 0;
+                _cardTempHigh = 0;
+
+                return true;
+            } else if(40 == _bitCount) {
+                _cardTempHigh >>= 1;
+
+                _code = _cardTemp;
+                _codeHigh = _cardTempHigh;
+
+                _wiegandType = _bitCount;
+                _bitCount = 0;
+                _cardTemp = 0;
+                _cardTempHigh = 0;
+
+                return true;
+            } else {
+                // wiegand 26 or wiegand 34
+                cardID = GetCardId(&_cardTempHigh, &_cardTemp, _bitCount);
+                _wiegandType = _bitCount;
+                _bitCount = 0;
+                _cardTemp = 0;
+                _cardTempHigh = 0;
+                _code = cardID;
+                return true;
+            }
+        } else {
+            // well time over 25 ms and bitCount !=8 , !=26, !=34 , must be noise or nothing then.
+            _lastWiegand = sysTick;
+            _bitCount = 0;
+            _cardTemp = 0;
+            _cardTempHigh = 0;
+            return false;
+        }
+    } else
+        return false;
+}

+ 27 - 0
applications/accessor/helpers/wiegand.h

@@ -0,0 +1,27 @@
+#pragma once
+
+class WIEGAND {
+public:
+    WIEGAND();
+    void begin();
+    bool available();
+    unsigned long getCode();
+    unsigned long getCodeHigh();
+    int getWiegandType();
+
+    static void ReadD0();
+    static void ReadD1();
+
+private:
+    static bool DoWiegandConversion();
+    static unsigned long
+    GetCardId(volatile unsigned long* codehigh, volatile unsigned long* codelow, char bitlength);
+
+    static volatile unsigned long _cardTempHigh;
+    static volatile unsigned long _cardTemp;
+    static volatile unsigned long _lastWiegand;
+    static volatile int _bitCount;
+    static int _wiegandType;
+    static unsigned long _code;
+    static unsigned long _codeHigh;
+};

+ 13 - 0
applications/accessor/scene/accessor-scene-generic.h

@@ -0,0 +1,13 @@
+#pragma once
+#include "../accessor-app.h"
+
+class AccessorApp;
+
+class AccessorScene {
+public:
+    virtual void on_enter(AccessorApp* app) = 0;
+    virtual bool on_event(AccessorApp* app, AccessorEvent* event) = 0;
+    virtual void on_exit(AccessorApp* app) = 0;
+
+private:
+};

+ 88 - 0
applications/accessor/scene/accessor-scene-start.cpp

@@ -0,0 +1,88 @@
+#include "../accessor-app.h"
+#include "../accessor-view-manager.h"
+#include "../accessor-event.h"
+#include <callback-connector.h>
+#include "accessor-scene-start.h"
+
+void AccessorSceneStart::on_enter(AccessorApp* app) {
+    AccessorAppViewManager* view_manager = app->get_view_manager();
+    Popup* popup = view_manager->get_popup();
+
+    popup_set_header(popup, "Accessor App", 64, 16, AlignCenter, AlignBottom);
+    app->set_text_store("[??????]");
+    popup_set_text(popup, app->get_text_store(), 64, 22, AlignCenter, AlignTop);
+
+    view_manager->switch_to(AccessorAppViewManager::ViewType::Popup);
+}
+
+bool AccessorSceneStart::on_event(AccessorApp* app, AccessorEvent* event) {
+    bool consumed = false;
+
+    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();
+
+        uint8_t data[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+        uint8_t type = 0;
+
+        if(wiegand->available()) {
+            type = wiegand->getWiegandType();
+
+            for(uint8_t i = 0; i < 4; i++) {
+                data[i] = wiegand->getCode() >> (i * 8);
+            }
+
+            for(uint8_t i = 4; i < 8; i++) {
+                data[i] = wiegand->getCodeHigh() >> ((i - 4) * 8);
+            }
+        } else {
+            __disable_irq();
+            if(onewire->reset()) {
+                type = 255;
+                onewire->write(0x33);
+                for(uint8_t i = 0; i < 8; i++) {
+                    data[i] = onewire->read();
+                }
+
+                for(uint8_t i = 0; i < 7; i++) {
+                    data[i] = data[i + 1];
+                }
+            }
+            __enable_irq();
+        }
+
+        if(type > 0) {
+            if(type == 255) {
+                app->set_text_store(
+                    "[%02X %02X %02X %02X %02X %02X DS]",
+                    data[5],
+                    data[4],
+                    data[3],
+                    data[2],
+                    data[1],
+                    data[0]);
+            } else {
+                app->set_text_store(
+                    "[%02X %02X %02X %02X %02X %02X W%u]",
+                    data[5],
+                    data[4],
+                    data[3],
+                    data[2],
+                    data[1],
+                    data[0],
+                    type);
+            }
+            popup_set_text(popup, app->get_text_store(), 64, 22, AlignCenter, AlignTop);
+            app->notify_success();
+        }
+    }
+
+    return consumed;
+}
+
+void AccessorSceneStart::on_exit(AccessorApp* app) {
+    Popup* popup = app->get_view_manager()->get_popup();
+    popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
+    popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
+}

+ 9 - 0
applications/accessor/scene/accessor-scene-start.h

@@ -0,0 +1,9 @@
+#pragma once
+#include "accessor-scene-generic.h"
+
+class AccessorSceneStart : public AccessorScene {
+public:
+    void on_enter(AccessorApp* app) final;
+    bool on_event(AccessorApp* app, AccessorEvent* event) final;
+    void on_exit(AccessorApp* app) final;
+};

+ 9 - 0
applications/applications.c

@@ -35,6 +35,7 @@ int32_t gui_test(void* p);
 int32_t keypad_test(void* p);
 int32_t scene_app(void* p);
 int32_t passport(void* p);
+int32_t app_accessor(void* p);
 
 const FlipperApplication FLIPPER_SERVICES[] = {
 #ifdef APP_CLI
@@ -149,6 +150,10 @@ const FlipperApplication FLIPPER_SERVICES[] = {
     {.app = keypad_test, .name = "keypad_test", .icon = A_Plugins_14},
 #endif
 
+#ifdef APP_ACCESSOR
+    {.app = app_accessor, .name = "accessor", .stack_size = 4096, .icon = A_Plugins_14},
+#endif
+
 };
 
 const size_t FLIPPER_SERVICES_COUNT = sizeof(FLIPPER_SERVICES) / sizeof(FlipperApplication);
@@ -230,6 +235,10 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = {
     {.app = keypad_test, .name = "keypad_test", .icon = A_Plugins_14},
 #endif
 
+#ifdef BUILD_ACCESSOR
+    {.app = app_accessor, .name = "accessor", .stack_size = 4096, .icon = A_Plugins_14},
+#endif
+
 };
 
 const size_t FLIPPER_DEBUG_APPS_COUNT = sizeof(FLIPPER_DEBUG_APPS) / sizeof(FlipperApplication);

+ 13 - 0
applications/applications.mk

@@ -25,6 +25,7 @@ BUILD_GPIO_DEMO = 1
 BUILD_MUSIC_PLAYER = 1
 BUILD_FLOOPPER_BLOOPPER = 1
 BUILD_IBUTTON = 1
+
 endif
 
 APP_DEBUG ?=0
@@ -32,6 +33,7 @@ ifeq ($(APP_DEBUG), 1)
 CFLAGS		+= -DAPP_DEBUG
 BUILD_GUI_TEST = 1
 BUILD_KEYPAD_TEST = 1
+BUILD_ACCESSOR = 1
 BUILD_SD_TEST = 1
 BUILD_VIBRO_DEMO = 1
 BUILD_SPEAKER_DEMO = 1
@@ -209,6 +211,17 @@ CFLAGS		+= -DBUILD_KEYPAD_TEST
 BUILD_KEYPAD_TEST = 1
 endif
 
+APP_ACCESSOR ?= 0
+ifeq ($(APP_ACCESSOR), 1)
+CFLAGS		+= -DAPP_ACCESSOR
+BUILD_ACCESSOR = 1
+endif
+BUILD_ACCESSOR ?= 0
+ifeq ($(BUILD_ACCESSOR), 1)
+CFLAGS		+= -DBUILD_ACCESSOR
+BUILD_ACCESSOR = 1
+endif
+
 APP_GPIO_DEMO ?= 0
 ifeq ($(APP_GPIO_DEMO), 1)
 CFLAGS		+= -DAPP_GPIO_DEMO

+ 10 - 0
firmware/targets/f5/api-hal/api-hal-resources.c

@@ -42,3 +42,13 @@ const GpioPin gpio_spi_d_sck = { .port=SPI_D_SCK_GPIO_Port, .pin=SPI_D_SCK_Pin }
 const GpioPin gpio_spi_r_miso = { .port=SPI_R_MISO_GPIO_Port, .pin=SPI_R_MISO_Pin };
 const GpioPin gpio_spi_r_mosi = { .port=SPI_R_MOSI_GPIO_Port, .pin=SPI_R_MOSI_Pin };
 const GpioPin gpio_spi_r_sck = { .port=SPI_R_SCK_GPIO_Port, .pin=SPI_R_SCK_Pin };
+
+// external gpio's
+const GpioPin ext_pc0_gpio = {.port = GPIOC, .pin = GPIO_PIN_0};
+const GpioPin ext_pc1_gpio = {.port = GPIOC, .pin = GPIO_PIN_1};
+const GpioPin ext_pc3_gpio = {.port = GPIOC, .pin = GPIO_PIN_3};
+const GpioPin ext_pb2_gpio = {.port = GPIOB, .pin = GPIO_PIN_2};
+const GpioPin ext_pb3_gpio = {.port = GPIOB, .pin = GPIO_PIN_3};
+const GpioPin ext_pa4_gpio = {.port = GPIOA, .pin = GPIO_PIN_4};
+const GpioPin ext_pa6_gpio = {.port = GPIOA, .pin = GPIO_PIN_6};
+const GpioPin ext_pa7_gpio = {.port = GPIOA, .pin = GPIO_PIN_7};

+ 8 - 0
firmware/targets/f5/api-hal/api-hal-resources.h

@@ -74,6 +74,14 @@ extern const GpioPin gpio_spi_r_miso;
 extern const GpioPin gpio_spi_r_mosi;
 extern const GpioPin gpio_spi_r_sck;
 
+extern const GpioPin ext_pc0_gpio;
+extern const GpioPin ext_pc1_gpio;
+extern const GpioPin ext_pc3_gpio;
+extern const GpioPin ext_pb2_gpio;
+extern const GpioPin ext_pb3_gpio;
+extern const GpioPin ext_pa4_gpio;
+extern const GpioPin ext_pa6_gpio;
+extern const GpioPin ext_pa7_gpio;
 
 #ifdef __cplusplus
 }