mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-07-16 00:18:11 -07:00
Merge remote-tracking branch 'ofw/dev' into yeet-lfs
This commit is contained in:
@@ -33,7 +33,6 @@ libs = env.BuildModules(
|
||||
"digital_signal",
|
||||
"pulse_reader",
|
||||
"signal_reader",
|
||||
"appframe",
|
||||
"u8g2",
|
||||
"lfrfid",
|
||||
"flipper_application",
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
template <typename TApp>
|
||||
class GenericScene {
|
||||
public:
|
||||
virtual void on_enter(TApp* app, bool need_restore) = 0;
|
||||
virtual bool on_event(TApp* app, typename TApp::Event* event) = 0;
|
||||
virtual void on_exit(TApp* app) = 0;
|
||||
virtual ~GenericScene() {};
|
||||
|
||||
private:
|
||||
};
|
||||
@@ -1,47 +0,0 @@
|
||||
#pragma once
|
||||
#include <core/record.h>
|
||||
|
||||
/**
|
||||
* @brief Class for opening, casting, holding and closing records
|
||||
*
|
||||
* @tparam TRecordClass record class
|
||||
*/
|
||||
template <typename TRecordClass>
|
||||
class RecordController {
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new Record Controller object for record with record name
|
||||
*
|
||||
* @param record_name record name
|
||||
*/
|
||||
RecordController(const char* record_name) {
|
||||
name = record_name;
|
||||
value = static_cast<TRecordClass*>(furi_record_open(name));
|
||||
}
|
||||
|
||||
~RecordController() {
|
||||
furi_record_close(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Record getter
|
||||
*
|
||||
* @return TRecordClass* record value
|
||||
*/
|
||||
TRecordClass* get() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Record getter (by cast)
|
||||
*
|
||||
* @return TRecordClass* record value
|
||||
*/
|
||||
operator TRecordClass*() const {
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
const char* name;
|
||||
TRecordClass* value;
|
||||
};
|
||||
@@ -1,246 +0,0 @@
|
||||
#include <map>
|
||||
#include <forward_list>
|
||||
#include <initializer_list>
|
||||
|
||||
#define GENERIC_SCENE_ENUM_VALUES Exit, Start
|
||||
#define GENERIC_EVENT_ENUM_VALUES Tick, Back
|
||||
|
||||
/**
|
||||
* @brief Controller for scene navigation in application
|
||||
*
|
||||
* @tparam TScene generic scene class
|
||||
* @tparam TApp application class
|
||||
*/
|
||||
template <typename TScene, typename TApp>
|
||||
class SceneController {
|
||||
public:
|
||||
/**
|
||||
* @brief Add scene to scene container
|
||||
*
|
||||
* @param scene_index scene index
|
||||
* @param scene_pointer scene object pointer
|
||||
*/
|
||||
void add_scene(typename TApp::SceneType scene_index, TScene* scene_pointer) {
|
||||
furi_check(scenes.count(scene_index) == 0);
|
||||
scenes[scene_index] = scene_pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Switch to next scene and store current scene in previous scenes list
|
||||
*
|
||||
* @param scene_index next scene index
|
||||
* @param need_restore true, if we want the scene to restore its parameters
|
||||
*/
|
||||
void switch_to_next_scene(typename TApp::SceneType scene_index, bool need_restore = false) {
|
||||
previous_scenes_list.push_front(current_scene_index);
|
||||
switch_to_scene(scene_index, need_restore);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Switch to next scene without ability to return to current scene
|
||||
*
|
||||
* @param scene_index next scene index
|
||||
* @param need_restore true, if we want the scene to restore its parameters
|
||||
*/
|
||||
void switch_to_scene(typename TApp::SceneType scene_index, bool need_restore = false) {
|
||||
if(scene_index != TApp::SceneType::Exit) {
|
||||
scenes[current_scene_index]->on_exit(app);
|
||||
current_scene_index = scene_index;
|
||||
scenes[current_scene_index]->on_enter(app, need_restore);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Search the scene in the list of previous scenes and switch to it
|
||||
*
|
||||
* @param scene_index_list list of scene indexes to which you want to switch
|
||||
*/
|
||||
bool search_and_switch_to_previous_scene(
|
||||
const std::initializer_list<typename TApp::SceneType>& scene_index_list) {
|
||||
auto previous_scene_index = TApp::SceneType::Exit;
|
||||
bool scene_found = false;
|
||||
bool result = false;
|
||||
|
||||
while(!scene_found) {
|
||||
previous_scene_index = get_previous_scene_index();
|
||||
for(const auto& element : scene_index_list) {
|
||||
if(previous_scene_index == element) {
|
||||
scene_found = true;
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(previous_scene_index == TApp::SceneType::Exit) {
|
||||
scene_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(result) {
|
||||
switch_to_scene(previous_scene_index, true);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool search_and_switch_to_another_scene(
|
||||
const std::initializer_list<typename TApp::SceneType>& scene_index_list,
|
||||
typename TApp::SceneType scene_index) {
|
||||
auto previous_scene_index = TApp::SceneType::Exit;
|
||||
bool scene_found = false;
|
||||
bool result = false;
|
||||
|
||||
while(!scene_found) {
|
||||
previous_scene_index = get_previous_scene_index();
|
||||
for(const auto& element : scene_index_list) {
|
||||
if(previous_scene_index == element) {
|
||||
scene_found = true;
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(previous_scene_index == TApp::SceneType::Exit) {
|
||||
scene_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(result) {
|
||||
switch_to_scene(scene_index, true);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool has_previous_scene(
|
||||
const std::initializer_list<typename TApp::SceneType>& scene_index_list) {
|
||||
bool result = false;
|
||||
|
||||
for(auto const& previous_element : previous_scenes_list) {
|
||||
for(const auto& element : scene_index_list) {
|
||||
if(previous_element == element) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(previous_element == TApp::SceneType::Exit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(result) break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start application main cycle
|
||||
*
|
||||
* @param tick_length_ms tick event length in milliseconds
|
||||
*/
|
||||
void process(
|
||||
uint32_t /* tick_length_ms */ = 100,
|
||||
typename TApp::SceneType start_scene_index = TApp::SceneType::Start) {
|
||||
typename TApp::Event event;
|
||||
bool consumed;
|
||||
bool exit = false;
|
||||
|
||||
current_scene_index = start_scene_index;
|
||||
scenes[current_scene_index]->on_enter(app, false);
|
||||
|
||||
while(!exit) {
|
||||
app->view_controller.receive_event(&event);
|
||||
|
||||
consumed = scenes[current_scene_index]->on_event(app, &event);
|
||||
|
||||
if(!consumed) {
|
||||
if(event.type == TApp::EventType::Back) {
|
||||
exit = switch_to_previous_scene();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
scenes[current_scene_index]->on_exit(app);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Switch to previous scene
|
||||
*
|
||||
* @param count how many steps back
|
||||
* @return true if app need to exit
|
||||
*/
|
||||
bool switch_to_previous_scene(uint8_t count = 1) {
|
||||
auto previous_scene_index = TApp::SceneType::Start;
|
||||
|
||||
for(uint8_t i = 0; i < count; i++)
|
||||
previous_scene_index = get_previous_scene_index();
|
||||
|
||||
if(previous_scene_index == TApp::SceneType::Exit) return true;
|
||||
|
||||
switch_to_scene(previous_scene_index, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct a new Scene Controller object
|
||||
*
|
||||
* @param app_pointer pointer to application class
|
||||
*/
|
||||
SceneController(TApp* app_pointer) {
|
||||
app = app_pointer;
|
||||
current_scene_index = TApp::SceneType::Exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroy the Scene Controller object
|
||||
*
|
||||
*/
|
||||
~SceneController() {
|
||||
for(auto& it : scenes)
|
||||
delete it.second;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Scenes pointers container
|
||||
*
|
||||
*/
|
||||
std::map<typename TApp::SceneType, TScene*> scenes;
|
||||
|
||||
/**
|
||||
* @brief List of indexes of previous scenes
|
||||
*
|
||||
*/
|
||||
std::forward_list<typename TApp::SceneType> previous_scenes_list;
|
||||
|
||||
/**
|
||||
* @brief Current scene index holder
|
||||
*
|
||||
*/
|
||||
typename TApp::SceneType current_scene_index;
|
||||
|
||||
/**
|
||||
* @brief Application pointer holder
|
||||
*
|
||||
*/
|
||||
TApp* app;
|
||||
|
||||
/**
|
||||
* @brief Get the previous scene index
|
||||
*
|
||||
* @return previous scene index
|
||||
*/
|
||||
typename TApp::SceneType get_previous_scene_index() {
|
||||
auto scene_index = TApp::SceneType::Exit;
|
||||
|
||||
if(!previous_scenes_list.empty()) {
|
||||
scene_index = previous_scenes_list.front();
|
||||
previous_scenes_list.pop_front();
|
||||
}
|
||||
|
||||
return scene_index;
|
||||
}
|
||||
};
|
||||
@@ -1,18 +0,0 @@
|
||||
#include "text_store.h"
|
||||
#include <furi.h>
|
||||
|
||||
TextStore::TextStore(uint8_t _text_size)
|
||||
: text_size(_text_size) {
|
||||
text = static_cast<char*>(malloc(text_size + 1));
|
||||
}
|
||||
|
||||
TextStore::~TextStore() {
|
||||
free(text);
|
||||
}
|
||||
|
||||
void TextStore::set(const char* _text...) {
|
||||
va_list args;
|
||||
va_start(args, _text);
|
||||
vsnprintf(text, text_size, _text, args);
|
||||
va_end(args);
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
class TextStore {
|
||||
public:
|
||||
TextStore(uint8_t text_size);
|
||||
~TextStore(void);
|
||||
|
||||
void set(const char* text...);
|
||||
const uint8_t text_size;
|
||||
char* text;
|
||||
};
|
||||
@@ -1,129 +0,0 @@
|
||||
/*
|
||||
* type_index without RTTI
|
||||
*
|
||||
* Copyright frickiericker 2016.
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person or organization
|
||||
* obtaining a copy of the software and accompanying documentation covered by
|
||||
* this license (the "Software") to use, reproduce, display, distribute,
|
||||
* execute, and transmit the Software, and to prepare derivative works of the
|
||||
* Software, and to permit third-parties to whom the Software is furnished to
|
||||
* do so, all subject to the following:
|
||||
*
|
||||
* The copyright notices in the Software and this entire statement, including
|
||||
* the above license grant, this restriction and the following disclaimer,
|
||||
* must be included in all copies of the Software, in whole or in part, and
|
||||
* all derivative works of the Software, unless such copies or derivative
|
||||
* works are solely in the form of machine-executable object code generated by
|
||||
* a source language processor.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
* SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
* FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace ext {
|
||||
/**
|
||||
* Dummy type for tag-dispatching.
|
||||
*/
|
||||
template <typename T>
|
||||
struct tag_type {};
|
||||
|
||||
/**
|
||||
* A value of tag_type<T>.
|
||||
*/
|
||||
template <typename T>
|
||||
constexpr tag_type<T> tag{};
|
||||
|
||||
/**
|
||||
* A type_index implementation without RTTI.
|
||||
*/
|
||||
struct type_index {
|
||||
/**
|
||||
* Creates a type_index object for the specified type.
|
||||
*/
|
||||
template <typename T>
|
||||
type_index(tag_type<T>) noexcept
|
||||
: hash_code_{index<T>} {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hash code.
|
||||
*/
|
||||
std::size_t hash_code() const noexcept {
|
||||
return hash_code_;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Unique integral index associated to template type argument.
|
||||
*/
|
||||
template <typename T>
|
||||
static std::size_t const index;
|
||||
|
||||
/**
|
||||
* Global counter for generating index values.
|
||||
*/
|
||||
static std::size_t& counter() noexcept {
|
||||
static std::size_t counter_;
|
||||
return counter_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::size_t hash_code_;
|
||||
};
|
||||
|
||||
template <typename>
|
||||
std::size_t const type_index::index = type_index::counter()++;
|
||||
|
||||
/**
|
||||
* Creates a type_index object for the specified type.
|
||||
*
|
||||
* Equivalent to `ext::type_index{ext::tag<T>}`.
|
||||
*/
|
||||
template <typename T>
|
||||
type_index make_type_index() noexcept {
|
||||
return tag<T>;
|
||||
}
|
||||
|
||||
inline bool operator==(type_index const& a, type_index const& b) noexcept {
|
||||
return a.hash_code() == b.hash_code();
|
||||
}
|
||||
|
||||
inline bool operator!=(type_index const& a, type_index const& b) noexcept {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
inline bool operator<(type_index const& a, type_index const& b) noexcept {
|
||||
return a.hash_code() < b.hash_code();
|
||||
}
|
||||
|
||||
inline bool operator<=(type_index const& a, type_index const& b) noexcept {
|
||||
return a.hash_code() <= b.hash_code();
|
||||
}
|
||||
|
||||
inline bool operator>(type_index const& a, type_index const& b) noexcept {
|
||||
return !(a <= b);
|
||||
}
|
||||
|
||||
inline bool operator>=(type_index const& a, type_index const& b) noexcept {
|
||||
return !(a < b);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
struct std::hash<ext::type_index> {
|
||||
using argument_type = ext::type_index;
|
||||
using result_type = std::size_t;
|
||||
|
||||
result_type operator()(argument_type const& t) const noexcept {
|
||||
return t.hash_code();
|
||||
}
|
||||
};
|
||||
@@ -1,170 +0,0 @@
|
||||
#pragma once
|
||||
#include "view_modules/generic_view_module.h"
|
||||
#include <map>
|
||||
#include <core/check.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <callback-connector.h>
|
||||
#include "typeindex_no_rtti.hpp"
|
||||
|
||||
/**
|
||||
* @brief Controller for switching application views and handling inputs and events
|
||||
*
|
||||
* @tparam TApp application class
|
||||
* @tparam TViewModules variadic list of ViewModules
|
||||
*/
|
||||
template <typename TApp, typename... TViewModules>
|
||||
class ViewController {
|
||||
public:
|
||||
ViewController() {
|
||||
event_queue = furi_message_queue_alloc(10, sizeof(typename TApp::Event));
|
||||
|
||||
view_dispatcher = view_dispatcher_alloc();
|
||||
previous_view_callback_pointer = cbc::obtain_connector(
|
||||
this, &ViewController<TApp, TViewModules...>::previous_view_callback);
|
||||
|
||||
[](...) {
|
||||
}((this->add_view(ext::make_type_index<TViewModules>().hash_code(), new TViewModules()),
|
||||
0)...);
|
||||
|
||||
gui = static_cast<Gui*>(furi_record_open("gui"));
|
||||
}
|
||||
|
||||
~ViewController() {
|
||||
for(auto& it : holder) {
|
||||
view_dispatcher_remove_view(view_dispatcher, static_cast<uint32_t>(it.first));
|
||||
delete it.second;
|
||||
}
|
||||
|
||||
view_dispatcher_free(view_dispatcher);
|
||||
furi_message_queue_free(event_queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get ViewModule pointer
|
||||
*
|
||||
* @tparam T Concrete ViewModule class
|
||||
* @return T* ViewModule pointer
|
||||
*/
|
||||
template <typename T>
|
||||
T* get() {
|
||||
uint32_t view_index = ext::make_type_index<T>().hash_code();
|
||||
furi_check(holder.count(view_index) != 0);
|
||||
return static_cast<T*>(holder[view_index]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get ViewModule pointer by cast
|
||||
*
|
||||
* @tparam T Concrete ViewModule class
|
||||
* @return T* ViewModule pointer
|
||||
*/
|
||||
template <typename T>
|
||||
operator T*() {
|
||||
uint32_t view_index = ext::make_type_index<T>().hash_code();
|
||||
furi_check(holder.count(view_index) != 0);
|
||||
return static_cast<T*>(holder[view_index]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Switch view to ViewModule
|
||||
*
|
||||
* @tparam T Concrete ViewModule class
|
||||
* @return T* ViewModule pointer
|
||||
*/
|
||||
template <typename T>
|
||||
void switch_to() {
|
||||
uint32_t view_index = ext::make_type_index<T>().hash_code();
|
||||
furi_check(holder.count(view_index) != 0);
|
||||
view_dispatcher_switch_to_view(view_dispatcher, view_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Receive event from app event queue
|
||||
*
|
||||
* @param event event pointer
|
||||
*/
|
||||
void receive_event(typename TApp::Event* event) {
|
||||
if(furi_message_queue_get(event_queue, event, 100) != FuriStatusOk) {
|
||||
event->type = TApp::EventType::Tick;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send event to app event queue
|
||||
*
|
||||
* @param event event pointer
|
||||
*/
|
||||
void send_event(typename TApp::Event* event) {
|
||||
FuriStatus result = furi_message_queue_put(event_queue, event, FuriWaitForever);
|
||||
furi_check(result == FuriStatusOk);
|
||||
}
|
||||
|
||||
void attach_to_gui(ViewDispatcherType type) {
|
||||
view_dispatcher_attach_to_gui(view_dispatcher, gui, type);
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief ViewModulesHolder
|
||||
*
|
||||
*/
|
||||
std::map<size_t, GenericViewModule*> holder;
|
||||
|
||||
/**
|
||||
* @brief App event queue
|
||||
*
|
||||
*/
|
||||
FuriMessageQueue* event_queue;
|
||||
|
||||
/**
|
||||
* @brief Main ViewDispatcher pointer
|
||||
*
|
||||
*/
|
||||
ViewDispatcher* view_dispatcher;
|
||||
|
||||
/**
|
||||
* @brief Gui record pointer
|
||||
*
|
||||
*/
|
||||
Gui* gui;
|
||||
|
||||
/**
|
||||
* @brief Previous view callback fn pointer
|
||||
*
|
||||
*/
|
||||
ViewNavigationCallback previous_view_callback_pointer;
|
||||
|
||||
/**
|
||||
* @brief Previous view callback fn
|
||||
*
|
||||
* @param context not used
|
||||
* @return uint32_t VIEW_IGNORE
|
||||
*/
|
||||
uint32_t previous_view_callback(void* context) {
|
||||
(void)context;
|
||||
|
||||
typename TApp::Event event;
|
||||
event.type = TApp::EventType::Back;
|
||||
|
||||
if(event_queue != NULL) {
|
||||
send_event(&event);
|
||||
}
|
||||
|
||||
return VIEW_IGNORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add ViewModule to holder
|
||||
*
|
||||
* @param view_index view index in holder
|
||||
* @param view_module view module pointer
|
||||
*/
|
||||
void add_view(size_t view_index, GenericViewModule* view_module) {
|
||||
furi_check(holder.count(view_index) == 0);
|
||||
holder[view_index] = view_module;
|
||||
|
||||
View* view = view_module->get_view();
|
||||
view_dispatcher_add_view(view_dispatcher, static_cast<uint32_t>(view_index), view);
|
||||
view_set_previous_callback(view, previous_view_callback_pointer);
|
||||
}
|
||||
};
|
||||
@@ -1,32 +0,0 @@
|
||||
#include "byte_input_vm.h"
|
||||
|
||||
ByteInputVM::ByteInputVM() {
|
||||
byte_input = byte_input_alloc();
|
||||
}
|
||||
|
||||
ByteInputVM::~ByteInputVM() {
|
||||
byte_input_free(byte_input);
|
||||
}
|
||||
|
||||
View* ByteInputVM::get_view() {
|
||||
return byte_input_get_view(byte_input);
|
||||
}
|
||||
|
||||
void ByteInputVM::clean() {
|
||||
byte_input_set_header_text(byte_input, "");
|
||||
byte_input_set_result_callback(byte_input, NULL, NULL, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
void ByteInputVM::set_result_callback(
|
||||
ByteInputCallback input_callback,
|
||||
ByteChangedCallback changed_callback,
|
||||
void* callback_context,
|
||||
uint8_t* bytes,
|
||||
uint8_t bytes_count) {
|
||||
byte_input_set_result_callback(
|
||||
byte_input, input_callback, changed_callback, callback_context, bytes, bytes_count);
|
||||
}
|
||||
|
||||
void ByteInputVM::set_header_text(const char* text) {
|
||||
byte_input_set_header_text(byte_input, text);
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
#pragma once
|
||||
#include "generic_view_module.h"
|
||||
#include <gui/modules/byte_input.h>
|
||||
|
||||
class ByteInputVM : public GenericViewModule {
|
||||
public:
|
||||
ByteInputVM(void);
|
||||
~ByteInputVM() final;
|
||||
View* get_view() final;
|
||||
void clean() final;
|
||||
|
||||
/**
|
||||
* @brief Set byte input result callback
|
||||
*
|
||||
* @param input_callback input callback fn
|
||||
* @param changed_callback changed callback fn
|
||||
* @param callback_context callback context
|
||||
* @param bytes buffer to use
|
||||
* @param bytes_count buffer length
|
||||
*/
|
||||
void set_result_callback(
|
||||
ByteInputCallback input_callback,
|
||||
ByteChangedCallback changed_callback,
|
||||
void* callback_context,
|
||||
uint8_t* bytes,
|
||||
uint8_t bytes_count);
|
||||
|
||||
/**
|
||||
* @brief Set byte input header text
|
||||
*
|
||||
* @param text text to be shown
|
||||
*/
|
||||
void set_header_text(const char* text);
|
||||
|
||||
private:
|
||||
ByteInput* byte_input;
|
||||
};
|
||||
@@ -1,61 +0,0 @@
|
||||
#include "dialog_ex_vm.h"
|
||||
|
||||
DialogExVM::DialogExVM() {
|
||||
dialog_ex = dialog_ex_alloc();
|
||||
}
|
||||
|
||||
DialogExVM::~DialogExVM() {
|
||||
dialog_ex_free(dialog_ex);
|
||||
}
|
||||
|
||||
View* DialogExVM::get_view() {
|
||||
return dialog_ex_get_view(dialog_ex);
|
||||
}
|
||||
|
||||
void DialogExVM::clean() {
|
||||
set_result_callback(NULL);
|
||||
set_context(NULL);
|
||||
set_header(NULL, 0, 0, AlignLeft, AlignBottom);
|
||||
set_text(NULL, 0, 0, AlignLeft, AlignBottom);
|
||||
set_icon(0, 0, NULL);
|
||||
set_left_button_text(NULL);
|
||||
set_center_button_text(NULL);
|
||||
set_right_button_text(NULL);
|
||||
}
|
||||
|
||||
void DialogExVM::set_result_callback(DialogExResultCallback callback) {
|
||||
dialog_ex_set_result_callback(dialog_ex, callback);
|
||||
}
|
||||
|
||||
void DialogExVM::set_context(void* context) {
|
||||
dialog_ex_set_context(dialog_ex, context);
|
||||
}
|
||||
|
||||
void DialogExVM::set_header(
|
||||
const char* text,
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
Align horizontal,
|
||||
Align vertical) {
|
||||
dialog_ex_set_header(dialog_ex, text, x, y, horizontal, vertical);
|
||||
}
|
||||
|
||||
void DialogExVM::set_text(const char* text, uint8_t x, uint8_t y, Align horizontal, Align vertical) {
|
||||
dialog_ex_set_text(dialog_ex, text, x, y, horizontal, vertical);
|
||||
}
|
||||
|
||||
void DialogExVM::set_icon(uint8_t x, uint8_t y, const Icon* icon) {
|
||||
dialog_ex_set_icon(dialog_ex, x, y, icon);
|
||||
}
|
||||
|
||||
void DialogExVM::set_left_button_text(const char* text) {
|
||||
dialog_ex_set_left_button_text(dialog_ex, text);
|
||||
}
|
||||
|
||||
void DialogExVM::set_center_button_text(const char* text) {
|
||||
dialog_ex_set_center_button_text(dialog_ex, text);
|
||||
}
|
||||
|
||||
void DialogExVM::set_right_button_text(const char* text) {
|
||||
dialog_ex_set_right_button_text(dialog_ex, text);
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
#pragma once
|
||||
#include "generic_view_module.h"
|
||||
#include <gui/modules/dialog_ex.h>
|
||||
|
||||
class DialogExVM : public GenericViewModule {
|
||||
public:
|
||||
DialogExVM(void);
|
||||
~DialogExVM() final;
|
||||
View* get_view() final;
|
||||
void clean() final;
|
||||
|
||||
/**
|
||||
* Set dialog result callback
|
||||
* @param callback - result callback function
|
||||
*/
|
||||
void set_result_callback(DialogExResultCallback callback);
|
||||
|
||||
/**
|
||||
* Set dialog context
|
||||
* @param context - context pointer, will be passed to result callback
|
||||
*/
|
||||
void set_context(void* context);
|
||||
|
||||
/**
|
||||
* Set dialog header text
|
||||
* If text is null, dialog header will not be rendered
|
||||
* @param text - text to be shown, can be multiline
|
||||
* @param x, y - text position
|
||||
* @param horizontal, vertical - text aligment
|
||||
*/
|
||||
void set_header(const char* text, uint8_t x, uint8_t y, Align horizontal, Align vertical);
|
||||
|
||||
/**
|
||||
* Set dialog text
|
||||
* If text is null, dialog text will not be rendered
|
||||
* @param text - text to be shown, can be multiline
|
||||
* @param x, y - text position
|
||||
* @param horizontal, vertical - text aligment
|
||||
*/
|
||||
void set_text(const char* text, uint8_t x, uint8_t y, Align horizontal, Align vertical);
|
||||
|
||||
/**
|
||||
* Set dialog icon
|
||||
* If x or y is negative, dialog icon will not be rendered
|
||||
* @param x, y - icon position
|
||||
* @param name - icon to be shown
|
||||
*/
|
||||
void set_icon(uint8_t x, uint8_t y, const Icon* icon);
|
||||
|
||||
/**
|
||||
* Set left button text
|
||||
* If text is null, left button will not be rendered and processed
|
||||
* @param text - text to be shown
|
||||
*/
|
||||
void set_left_button_text(const char* text);
|
||||
|
||||
/**
|
||||
* Set center button text
|
||||
* If text is null, center button will not be rendered and processed
|
||||
* @param text - text to be shown
|
||||
*/
|
||||
void set_center_button_text(const char* text);
|
||||
|
||||
/**
|
||||
* Set right button text
|
||||
* If text is null, right button will not be rendered and processed
|
||||
* @param text - text to be shown
|
||||
*/
|
||||
void set_right_button_text(const char* text);
|
||||
|
||||
private:
|
||||
DialogEx* dialog_ex;
|
||||
};
|
||||
@@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
#include <gui/view.h>
|
||||
|
||||
class GenericViewModule {
|
||||
public:
|
||||
GenericViewModule() {};
|
||||
virtual ~GenericViewModule() {};
|
||||
virtual View* get_view() = 0;
|
||||
virtual void clean() = 0;
|
||||
};
|
||||
@@ -1,56 +0,0 @@
|
||||
#include "popup_vm.h"
|
||||
#include <gui/modules/popup.h>
|
||||
|
||||
PopupVM::PopupVM() {
|
||||
popup = popup_alloc();
|
||||
}
|
||||
|
||||
PopupVM::~PopupVM() {
|
||||
popup_free(popup);
|
||||
}
|
||||
|
||||
View* PopupVM::get_view() {
|
||||
return popup_get_view(popup);
|
||||
}
|
||||
|
||||
void PopupVM::clean() {
|
||||
set_callback(NULL);
|
||||
set_context(NULL);
|
||||
set_header(NULL, 0, 0, AlignLeft, AlignBottom);
|
||||
set_text(NULL, 0, 0, AlignLeft, AlignBottom);
|
||||
set_icon(0, 0, NULL);
|
||||
disable_timeout();
|
||||
set_timeout(1000);
|
||||
}
|
||||
|
||||
void PopupVM::set_callback(PopupCallback callback) {
|
||||
popup_set_callback(popup, callback);
|
||||
}
|
||||
|
||||
void PopupVM::set_context(void* context) {
|
||||
popup_set_context(popup, context);
|
||||
}
|
||||
|
||||
void PopupVM::set_header(const char* text, uint8_t x, uint8_t y, Align horizontal, Align vertical) {
|
||||
popup_set_header(popup, text, x, y, horizontal, vertical);
|
||||
}
|
||||
|
||||
void PopupVM::set_text(const char* text, uint8_t x, uint8_t y, Align horizontal, Align vertical) {
|
||||
popup_set_text(popup, text, x, y, horizontal, vertical);
|
||||
}
|
||||
|
||||
void PopupVM::set_icon(int8_t x, int8_t y, const Icon* icon) {
|
||||
popup_set_icon(popup, x, y, icon);
|
||||
}
|
||||
|
||||
void PopupVM::set_timeout(uint32_t timeout_in_ms) {
|
||||
popup_set_timeout(popup, timeout_in_ms);
|
||||
}
|
||||
|
||||
void PopupVM::enable_timeout() {
|
||||
popup_enable_timeout(popup);
|
||||
}
|
||||
|
||||
void PopupVM::disable_timeout() {
|
||||
popup_disable_timeout(popup);
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
#pragma once
|
||||
#include "generic_view_module.h"
|
||||
#include <gui/modules/popup.h>
|
||||
|
||||
class PopupVM : public GenericViewModule {
|
||||
public:
|
||||
PopupVM(void);
|
||||
~PopupVM() final;
|
||||
View* get_view() final;
|
||||
void clean() final;
|
||||
|
||||
/**
|
||||
* Set popup header text
|
||||
* @param text - text to be shown
|
||||
*/
|
||||
void set_callback(PopupCallback callback);
|
||||
|
||||
/**
|
||||
* Set popup context
|
||||
* @param context - context pointer, will be passed to result callback
|
||||
*/
|
||||
void set_context(void* context);
|
||||
|
||||
/**
|
||||
* Set popup header text
|
||||
* If text is null, popup header will not be rendered
|
||||
* @param text - text to be shown, can be multiline
|
||||
* @param x, y - text position
|
||||
* @param horizontal, vertical - text aligment
|
||||
*/
|
||||
void set_header(const char* text, uint8_t x, uint8_t y, Align horizontal, Align vertical);
|
||||
|
||||
/**
|
||||
* Set popup text
|
||||
* If text is null, popup text will not be rendered
|
||||
* @param text - text to be shown, can be multiline
|
||||
* @param x, y - text position
|
||||
* @param horizontal, vertical - text aligment
|
||||
*/
|
||||
void set_text(const char* text, uint8_t x, uint8_t y, Align horizontal, Align vertical);
|
||||
|
||||
/**
|
||||
* Set popup icon
|
||||
* If icon position is negative, popup icon will not be rendered
|
||||
* @param x, y - icon position
|
||||
* @param name - icon to be shown
|
||||
*/
|
||||
void set_icon(int8_t x, int8_t y, const Icon* icon);
|
||||
|
||||
/**
|
||||
* Set popup timeout
|
||||
* @param timeout_in_ms - popup timeout value in milliseconds
|
||||
*/
|
||||
void set_timeout(uint32_t timeout_in_ms);
|
||||
|
||||
/**
|
||||
* Enable popup timeout
|
||||
*/
|
||||
void enable_timeout(void);
|
||||
|
||||
/**
|
||||
* Disable popup timeout
|
||||
*/
|
||||
void disable_timeout(void);
|
||||
|
||||
private:
|
||||
Popup* popup;
|
||||
};
|
||||
@@ -1,33 +0,0 @@
|
||||
#include "submenu_vm.h"
|
||||
|
||||
SubmenuVM::SubmenuVM() {
|
||||
submenu = submenu_alloc();
|
||||
}
|
||||
|
||||
SubmenuVM::~SubmenuVM() {
|
||||
submenu_free(submenu);
|
||||
}
|
||||
|
||||
View* SubmenuVM::get_view() {
|
||||
return submenu_get_view(submenu);
|
||||
}
|
||||
|
||||
void SubmenuVM::clean() {
|
||||
submenu_reset(submenu);
|
||||
}
|
||||
|
||||
void SubmenuVM::add_item(
|
||||
const char* label,
|
||||
uint32_t index,
|
||||
SubmenuItemCallback callback,
|
||||
void* callback_context) {
|
||||
submenu_add_item(submenu, label, index, callback, callback_context);
|
||||
}
|
||||
|
||||
void SubmenuVM::set_selected_item(uint32_t index) {
|
||||
submenu_set_selected_item(submenu, index);
|
||||
}
|
||||
|
||||
void SubmenuVM::set_header(const char* header) {
|
||||
submenu_set_header(submenu, header);
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
#pragma once
|
||||
#include "generic_view_module.h"
|
||||
#include <gui/modules/submenu.h>
|
||||
|
||||
class SubmenuVM : public GenericViewModule {
|
||||
public:
|
||||
SubmenuVM(void);
|
||||
~SubmenuVM() final;
|
||||
View* get_view() final;
|
||||
void clean() final;
|
||||
|
||||
/**
|
||||
* @brief Add item to submenu
|
||||
*
|
||||
* @param label - menu item label
|
||||
* @param index - menu item index, used for callback, may be the same with other items
|
||||
* @param callback - menu item callback
|
||||
* @param callback_context - menu item callback context
|
||||
*/
|
||||
void add_item(
|
||||
const char* label,
|
||||
uint32_t index,
|
||||
SubmenuItemCallback callback,
|
||||
void* callback_context);
|
||||
|
||||
/**
|
||||
* @brief Set submenu item selector
|
||||
*
|
||||
* @param index index of the item to be selected
|
||||
*/
|
||||
void set_selected_item(uint32_t index);
|
||||
|
||||
/**
|
||||
* @brief Set optional header for submenu
|
||||
*
|
||||
* @param header header to set
|
||||
*/
|
||||
void set_header(const char* header);
|
||||
|
||||
private:
|
||||
Submenu* submenu;
|
||||
};
|
||||
@@ -1,39 +0,0 @@
|
||||
#include "text_input_vm.h"
|
||||
|
||||
TextInputVM::TextInputVM() {
|
||||
text_input = text_input_alloc();
|
||||
}
|
||||
|
||||
TextInputVM::~TextInputVM() {
|
||||
text_input_free(text_input);
|
||||
}
|
||||
|
||||
View* TextInputVM::get_view() {
|
||||
return text_input_get_view(text_input);
|
||||
}
|
||||
|
||||
void TextInputVM::clean() {
|
||||
text_input_reset(text_input);
|
||||
}
|
||||
|
||||
void TextInputVM::set_result_callback(
|
||||
TextInputCallback callback,
|
||||
void* callback_context,
|
||||
char* text,
|
||||
uint8_t max_text_length,
|
||||
bool clear_default_text) {
|
||||
text_input_set_result_callback(
|
||||
text_input, callback, callback_context, text, max_text_length, clear_default_text);
|
||||
}
|
||||
|
||||
void TextInputVM::set_header_text(const char* text) {
|
||||
text_input_set_header_text(text_input, text);
|
||||
}
|
||||
|
||||
void TextInputVM::set_validator(TextInputValidatorCallback callback, void* callback_context) {
|
||||
text_input_set_validator(text_input, callback, callback_context);
|
||||
}
|
||||
|
||||
void* TextInputVM::get_validator_callback_context() {
|
||||
return text_input_get_validator_callback_context(text_input);
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
#pragma once
|
||||
#include "generic_view_module.h"
|
||||
#include <gui/modules/text_input.h>
|
||||
|
||||
class TextInputVM : public GenericViewModule {
|
||||
public:
|
||||
TextInputVM(void);
|
||||
~TextInputVM() final;
|
||||
View* get_view() final;
|
||||
void clean() final;
|
||||
|
||||
/**
|
||||
* @brief Set text input result callback
|
||||
*
|
||||
* @param callback - callback fn
|
||||
* @param callback_context - callback context
|
||||
* @param text - text buffer to use
|
||||
* @param max_text_length - text buffer length
|
||||
* @param clear_default_text - clears given buffer on OK event
|
||||
*/
|
||||
void set_result_callback(
|
||||
TextInputCallback callback,
|
||||
void* callback_context,
|
||||
char* text,
|
||||
uint8_t max_text_length,
|
||||
bool clear_default_text);
|
||||
|
||||
/**
|
||||
* @brief Set text input header text
|
||||
*
|
||||
* @param text - text to be shown
|
||||
*/
|
||||
void set_header_text(const char* text);
|
||||
|
||||
void set_validator(TextInputValidatorCallback callback, void* callback_context);
|
||||
|
||||
void* get_validator_callback_context(void);
|
||||
|
||||
private:
|
||||
TextInput* text_input;
|
||||
};
|
||||
@@ -1,29 +0,0 @@
|
||||
Import("env")
|
||||
|
||||
env.Append(
|
||||
CPPPATH=[
|
||||
"#/lib/app-scened-template",
|
||||
"#/lib/callback-connector",
|
||||
],
|
||||
LINT_SOURCES=[
|
||||
Dir("app-scened-template"),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
libenv = env.Clone(FW_LIB_NAME="appframe")
|
||||
libenv.ApplyLibFlags()
|
||||
|
||||
sources = []
|
||||
|
||||
recurse_dirs = [
|
||||
"app-scened-template",
|
||||
"callback-connector",
|
||||
]
|
||||
|
||||
for recurse_dir in recurse_dirs:
|
||||
sources += libenv.GlobRecursive("*.c*", recurse_dir)
|
||||
|
||||
lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources)
|
||||
libenv.Install("${LIB_DIST_DIR}", lib)
|
||||
Return("lib")
|
||||
@@ -57,9 +57,12 @@ bool st25r3916_read_fifo(
|
||||
do {
|
||||
uint8_t fifo_status[2] = {};
|
||||
st25r3916_read_burst_regs(handle, ST25R3916_REG_FIFO_STATUS1, fifo_status, 2);
|
||||
size_t bytes = ((fifo_status[1] & ST25R3916_REG_FIFO_STATUS2_fifo_b_mask) >>
|
||||
ST25R3916_REG_FIFO_STATUS2_fifo_b_shift) |
|
||||
fifo_status[0];
|
||||
|
||||
uint16_t fifo_status_b9_b8 =
|
||||
((fifo_status[1] & ST25R3916_REG_FIFO_STATUS2_fifo_b_mask) >>
|
||||
ST25R3916_REG_FIFO_STATUS2_fifo_b_shift);
|
||||
size_t bytes = (fifo_status_b9_b8 << 8) | fifo_status[0];
|
||||
|
||||
uint8_t bits =
|
||||
((fifo_status[1] & ST25R3916_REG_FIFO_STATUS2_fifo_lb_mask) >>
|
||||
ST25R3916_REG_FIFO_STATUS2_fifo_lb_shift);
|
||||
|
||||
@@ -0,0 +1,385 @@
|
||||
#include "dickert_mahs.h"
|
||||
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <furi_hal_rtc.h>
|
||||
|
||||
#define TAG "SubGhzProtocolDicketMAHS"
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_dickert_mahs_const = {
|
||||
.te_short = 400,
|
||||
.te_long = 800,
|
||||
.te_delta = 100,
|
||||
.min_count_bit_for_found = 36,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderDickertMAHS {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
uint32_t tmp[2];
|
||||
uint8_t tmp_cnt;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderDickertMAHS {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
DickertMAHSDecoderStepReset = 0,
|
||||
DickertMAHSDecoderStepInitial,
|
||||
DickertMAHSDecoderStepRecording,
|
||||
} DickertMAHSDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_dickert_mahs_decoder = {
|
||||
.alloc = subghz_protocol_decoder_dickert_mahs_alloc,
|
||||
.free = subghz_protocol_decoder_dickert_mahs_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_dickert_mahs_feed,
|
||||
.reset = subghz_protocol_decoder_dickert_mahs_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_dickert_mahs_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_dickert_mahs_serialize,
|
||||
.deserialize = subghz_protocol_decoder_dickert_mahs_deserialize,
|
||||
.get_string = subghz_protocol_decoder_dickert_mahs_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_dickert_mahs_encoder = {
|
||||
.alloc = subghz_protocol_encoder_dickert_mahs_alloc,
|
||||
.free = subghz_protocol_encoder_dickert_mahs_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_dickert_mahs_deserialize,
|
||||
.stop = subghz_protocol_encoder_dickert_mahs_stop,
|
||||
.yield = subghz_protocol_encoder_dickert_mahs_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_dickert_mahs = {
|
||||
.name = SUBGHZ_PROTOCOL_DICKERT_MAHS_NAME,
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_dickert_mahs_decoder,
|
||||
.encoder = &subghz_protocol_dickert_mahs_encoder,
|
||||
};
|
||||
|
||||
static void subghz_protocol_encoder_dickert_mahs_parse_buffer(
|
||||
SubGhzProtocolDecoderDickertMAHS* instance,
|
||||
FuriString* output) {
|
||||
// We assume we have only decodes < 64 bit!
|
||||
uint64_t data = instance->generic.data;
|
||||
uint8_t bits[36] = {};
|
||||
|
||||
// Convert uint64_t into bit array
|
||||
for(int i = 35; i >= 0; i--) {
|
||||
if(data & 1) {
|
||||
bits[i] = 1;
|
||||
}
|
||||
data >>= 1;
|
||||
}
|
||||
|
||||
// Decode symbols
|
||||
FuriString* code = furi_string_alloc();
|
||||
for(size_t i = 0; i < 35; i += 2) {
|
||||
uint8_t dip = (bits[i] << 1) + bits[i + 1];
|
||||
// PLUS = 3, // 0b11
|
||||
// ZERO = 1, // 0b01
|
||||
// MINUS = 0, // 0x00
|
||||
if(dip == 0x01) {
|
||||
furi_string_cat(code, "0");
|
||||
} else if(dip == 0x00) {
|
||||
furi_string_cat(code, "-");
|
||||
} else if(dip == 0x03) {
|
||||
furi_string_cat(code, "+");
|
||||
} else {
|
||||
furi_string_cat(code, "?");
|
||||
}
|
||||
}
|
||||
|
||||
FuriString* user_dips = furi_string_alloc();
|
||||
FuriString* fact_dips = furi_string_alloc();
|
||||
furi_string_set_n(user_dips, code, 0, 10);
|
||||
furi_string_set_n(fact_dips, code, 10, 8);
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s\r\n"
|
||||
"User-Dips:\t%s\r\n"
|
||||
"Fac-Code:\t%s\r\n",
|
||||
instance->generic.protocol_name,
|
||||
furi_string_get_cstr(user_dips),
|
||||
furi_string_get_cstr(fact_dips));
|
||||
furi_string_free(user_dips);
|
||||
furi_string_free(fact_dips);
|
||||
furi_string_free(code);
|
||||
}
|
||||
|
||||
void* subghz_protocol_encoder_dickert_mahs_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderDickertMAHS* instance = malloc(sizeof(SubGhzProtocolEncoderDickertMAHS));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_dickert_mahs;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 128;
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_dickert_mahs_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderDickertMAHS* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderDickertMAHS instance
|
||||
* @return true On success
|
||||
*/
|
||||
static bool
|
||||
subghz_protocol_encoder_dickert_mahs_get_upload(SubGhzProtocolEncoderDickertMAHS* instance) {
|
||||
furi_assert(instance);
|
||||
size_t index = 0;
|
||||
size_t size_upload = (instance->generic.data_count_bit * 2) + 2;
|
||||
if(size_upload > instance->encoder.size_upload) {
|
||||
FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer.");
|
||||
return false;
|
||||
} else {
|
||||
instance->encoder.size_upload = size_upload;
|
||||
}
|
||||
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_dickert_mahs_const.te_short * 112);
|
||||
// Send start bit
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_dickert_mahs_const.te_short);
|
||||
|
||||
//Send key data
|
||||
for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {
|
||||
if(bit_read(instance->generic.data, i - 1)) {
|
||||
//send bit 1
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_dickert_mahs_const.te_long);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_dickert_mahs_const.te_short);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_dickert_mahs_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_dickert_mahs_const.te_long);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_encoder_dickert_mahs_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderDickertMAHS* instance = context;
|
||||
SubGhzProtocolStatus ret = SubGhzProtocolStatusError;
|
||||
do {
|
||||
ret = subghz_block_generic_deserialize(&instance->generic, flipper_format);
|
||||
if(ret != SubGhzProtocolStatusOk) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Allow for longer keys (<) instead of !=
|
||||
if((instance->generic.data_count_bit <
|
||||
subghz_protocol_dickert_mahs_const.min_count_bit_for_found)) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
ret = SubGhzProtocolStatusErrorValueBitCount;
|
||||
break;
|
||||
}
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
if(!subghz_protocol_encoder_dickert_mahs_get_upload(instance)) {
|
||||
ret = SubGhzProtocolStatusErrorEncoderGetUpload;
|
||||
break;
|
||||
}
|
||||
instance->encoder.is_running = true;
|
||||
} while(false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_dickert_mahs_stop(void* context) {
|
||||
SubGhzProtocolEncoderDickertMAHS* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_dickert_mahs_yield(void* context) {
|
||||
SubGhzProtocolEncoderDickertMAHS* instance = context;
|
||||
|
||||
if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {
|
||||
instance->encoder.is_running = false;
|
||||
return level_duration_reset();
|
||||
}
|
||||
|
||||
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
|
||||
|
||||
if(++instance->encoder.front == instance->encoder.size_upload) {
|
||||
instance->encoder.repeat--;
|
||||
instance->encoder.front = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_dickert_mahs_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderDickertMAHS* instance = malloc(sizeof(SubGhzProtocolDecoderDickertMAHS));
|
||||
instance->base.protocol = &subghz_protocol_dickert_mahs;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
instance->tmp_cnt = 0;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_dickert_mahs_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderDickertMAHS* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_dickert_mahs_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderDickertMAHS* instance = context;
|
||||
instance->decoder.parser_step = DickertMAHSDecoderStepReset;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_dickert_mahs_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderDickertMAHS* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case DickertMAHSDecoderStepReset:
|
||||
// Check if done
|
||||
if(instance->decoder.decode_count_bit >=
|
||||
subghz_protocol_dickert_mahs_const.min_count_bit_for_found) {
|
||||
instance->generic.serial = 0x0;
|
||||
instance->generic.btn = 0x0;
|
||||
|
||||
instance->generic.data = instance->decoder.decode_data;
|
||||
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
|
||||
|
||||
if(instance->base.callback)
|
||||
instance->base.callback(&instance->base, instance->base.context);
|
||||
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
}
|
||||
|
||||
if((!level) && (duration > 10 * subghz_protocol_dickert_mahs_const.te_short)) {
|
||||
//Found header DICKERT_MAHS
|
||||
instance->decoder.parser_step = DickertMAHSDecoderStepInitial;
|
||||
}
|
||||
break;
|
||||
case DickertMAHSDecoderStepInitial:
|
||||
if(!level) {
|
||||
break;
|
||||
} else if(
|
||||
DURATION_DIFF(duration, subghz_protocol_dickert_mahs_const.te_short) <
|
||||
subghz_protocol_dickert_mahs_const.te_delta) {
|
||||
//Found start bit DICKERT_MAHS
|
||||
instance->decoder.parser_step = DickertMAHSDecoderStepRecording;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
} else {
|
||||
instance->decoder.parser_step = DickertMAHSDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case DickertMAHSDecoderStepRecording:
|
||||
if((!level && instance->tmp_cnt == 0) || (level && instance->tmp_cnt == 1)) {
|
||||
instance->tmp[instance->tmp_cnt] = duration;
|
||||
|
||||
instance->tmp_cnt++;
|
||||
|
||||
if(instance->tmp_cnt == 2) {
|
||||
if(DURATION_DIFF(instance->tmp[0] + instance->tmp[1], 1200) <
|
||||
subghz_protocol_dickert_mahs_const.te_delta) {
|
||||
if(DURATION_DIFF(instance->tmp[0], subghz_protocol_dickert_mahs_const.te_long) <
|
||||
subghz_protocol_dickert_mahs_const.te_delta) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
} else if(
|
||||
DURATION_DIFF(
|
||||
instance->tmp[0], subghz_protocol_dickert_mahs_const.te_short) <
|
||||
subghz_protocol_dickert_mahs_const.te_delta) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
}
|
||||
|
||||
instance->tmp_cnt = 0;
|
||||
} else {
|
||||
instance->tmp_cnt = 0;
|
||||
instance->decoder.parser_step = DickertMAHSDecoderStepReset;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
instance->tmp_cnt = 0;
|
||||
instance->decoder.parser_step = DickertMAHSDecoderStepReset;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_dickert_mahs_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderDickertMAHS* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_dickert_mahs_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderDickertMAHS* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_decoder_dickert_mahs_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderDickertMAHS* instance = context;
|
||||
SubGhzProtocolStatus ret = SubGhzProtocolStatusError;
|
||||
do {
|
||||
ret = subghz_block_generic_deserialize(&instance->generic, flipper_format);
|
||||
if(ret != SubGhzProtocolStatusOk) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Allow for longer keys (<) instead of !=
|
||||
if((instance->generic.data_count_bit <
|
||||
subghz_protocol_dickert_mahs_const.min_count_bit_for_found)) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
ret = SubGhzProtocolStatusErrorValueBitCount;
|
||||
break;
|
||||
}
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_dickert_mahs_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
subghz_protocol_encoder_dickert_mahs_parse_buffer(context, output);
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_DICKERT_MAHS_NAME "Dickert_MAHS"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderDickertMAHS SubGhzProtocolDecoderDickertMAHS;
|
||||
typedef struct SubGhzProtocolEncoderDickertMAHS SubGhzProtocolEncoderDickertMAHS;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_dickert_mahs_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_dickert_mahs_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_dickert_mahs;
|
||||
|
||||
/** Allocate SubGhzProtocolEncoderDickertMAHS.
|
||||
*
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
*
|
||||
* @return pointer to a SubGhzProtocolEncoderDickertMAHS instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_dickert_mahs_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/** Free SubGhzProtocolEncoderDickertMAHS.
|
||||
*
|
||||
* @param context Pointer to a SubGhzProtocolEncoderDickertMAHS instance
|
||||
*/
|
||||
void subghz_protocol_encoder_dickert_mahs_free(void* context);
|
||||
|
||||
/** Deserialize and generating an upload to send.
|
||||
*
|
||||
* @param context Pointer to a SubGhzProtocolEncoderDickertMAHS
|
||||
* instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_encoder_dickert_mahs_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/** Forced transmission stop.
|
||||
*
|
||||
* @param context Pointer to a SubGhzProtocolEncoderDickertMAHS instance
|
||||
*/
|
||||
void subghz_protocol_encoder_dickert_mahs_stop(void* context);
|
||||
|
||||
/** Getting the level and duration of the upload to be loaded into DMA.
|
||||
*
|
||||
* @param context Pointer to a SubGhzProtocolEncoderDickertMAHS instance
|
||||
*
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_dickert_mahs_yield(void* context);
|
||||
|
||||
/** Allocate SubGhzProtocolDecoderDickertMAHS.
|
||||
*
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
*
|
||||
* @return pointer to a SubGhzProtocolDecoderDickertMAHS instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_dickert_mahs_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/** Free SubGhzProtocolDecoderDickertMAHS.
|
||||
*
|
||||
* @param context Pointer to a SubGhzProtocolDecoderDickertMAHS instance
|
||||
*/
|
||||
void subghz_protocol_decoder_dickert_mahs_free(void* context);
|
||||
|
||||
/** Reset decoder SubGhzProtocolDecoderDickertMAHS.
|
||||
*
|
||||
* @param context Pointer to a SubGhzProtocolDecoderDickertMAHS instance
|
||||
*/
|
||||
void subghz_protocol_decoder_dickert_mahs_reset(void* context);
|
||||
|
||||
/** Parse a raw sequence of levels and durations received from the air.
|
||||
*
|
||||
* @param context Pointer to a SubGhzProtocolDecoderDickertMAHS instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_dickert_mahs_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/** Getting the hash sum of the last randomly received parcel.
|
||||
*
|
||||
* @param context Pointer to a SubGhzProtocolDecoderDickertMAHS instance
|
||||
*
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_dickert_mahs_get_hash_data(void* context);
|
||||
|
||||
/** Serialize data SubGhzProtocolDecoderDickertMAHS.
|
||||
*
|
||||
* @param context Pointer to a SubGhzProtocolDecoderDickertMAHS
|
||||
* instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received,
|
||||
* SubGhzRadioPreset
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_dickert_mahs_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/** Deserialize data SubGhzProtocolDecoderDickertMAHS.
|
||||
*
|
||||
* @param context Pointer to a SubGhzProtocolDecoderDickertMAHS
|
||||
* instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_decoder_dickert_mahs_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/** Getting a textual representation of the received data.
|
||||
*
|
||||
* @param context Pointer to a SubGhzProtocolDecoderDickertMAHS instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_dickert_mahs_get_string(void* context, FuriString* output);
|
||||
@@ -72,6 +72,7 @@ const SubGhzProtocol* subghz_protocol_registry_items[] = {
|
||||
&subghz_protocol_x10,
|
||||
&subghz_protocol_hormann_bisecur,
|
||||
&subghz_protocol_legrand,
|
||||
&subghz_protocol_dickert_mahs,
|
||||
};
|
||||
|
||||
const SubGhzProtocolRegistry subghz_protocol_registry = {
|
||||
|
||||
@@ -73,3 +73,4 @@
|
||||
#include "x10.h"
|
||||
#include "hormann_bisecur.h"
|
||||
#include "legrand.h"
|
||||
#include "dickert_mahs.h"
|
||||
|
||||
@@ -41,3 +41,7 @@ typedef FuriEventFlag* FuriApiLock;
|
||||
#define api_lock_wait_unlock_and_free(_lock) \
|
||||
api_lock_wait_unlock(_lock); \
|
||||
api_lock_free(_lock);
|
||||
|
||||
#define api_lock_is_locked(_lock) (!(furi_event_flag_get(_lock) & API_LOCK_EVENT))
|
||||
|
||||
#define api_lock_relock(_lock) furi_event_flag_clear(_lock, API_LOCK_EVENT)
|
||||
|
||||
Reference in New Issue
Block a user