diff --git a/ReadMe.md b/ReadMe.md index 927369e07..01f154296 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,6 +1,8 @@ -# Flipper Zero Unleashed Firmware - -fzCUSTOM +

+ +fzCUSTOM + +

Welcome to Flipper Zero's Custom Firmware repo! Our goal is to make any features possible in this device without any limitations! @@ -57,7 +59,7 @@ See changelog in releases for latest updates! - ESP8266 Deauther plugin [(by SequoiaSan)](https://github.com/SequoiaSan/FlipperZero-Wifi-ESP8266-Deauther-Module) - WiFi Scanner plugin [(by SequoiaSan)](https://github.com/SequoiaSan/FlipperZero-WiFi-Scanner_Module) -- Dec/Hex Converter plugin [(by theisolinearchip)](https://github.com/theisolinearchip/flipperzero_stuff/tree/main/applications/dec_hex_converter) +- MultiConverter plugin [(by theisolinearchip)](https://github.com/theisolinearchip/flipperzero_stuff) - WAV player plugin (fixed) [(OFW: DrZlo13)](https://github.com/flipperdevices/flipperzero-firmware/tree/zlo/wav-player) - UPC-A Barcode generator plugin [(by McAzzaMan)](https://github.com/McAzzaMan/flipperzero-firmware/tree/UPC-A_Barcode_Generator/applications/barcode_generator) - GPIO: Sentry Safe plugin [(by H4ckd4ddy)](https://github.com/H4ckd4ddy/flipperzero-sentry-safe-plugin) @@ -91,6 +93,8 @@ See changelog in releases for latest updates! ## [- Barcode Generator](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/BarcodeGenerator.md) +## [- Multi Converter](https://github.com/Eng1n33r/flipperzero-firmware/blob/dev/documentation/MultiConverter.md) + ## [- WAV Player sample files & how to convert](https://github.com/UberGuidoZ/Flipper/tree/main/Wav_Player#readme) ### **Plugins that works with external hardware** diff --git a/applications/dec_hex_converter/dec_hex_converter.c b/applications/dec_hex_converter/dec_hex_converter.c deleted file mode 100644 index 98a090a82..000000000 --- a/applications/dec_hex_converter/dec_hex_converter.c +++ /dev/null @@ -1,404 +0,0 @@ -#include -#include -#include -#include - -#define DEC_HEX_CONVERTER_NUMBER_DIGITS 9 -#define DEC_HEX_CONVERTER_KEYS 18 -#define DEC_HEX_CONVERTER_KEY_DEL 16 -// #define DEC_HEX_CONVERTER_KEY_SWAP 17 // actually not used... - -#define DEC_HEX_CONVERTER_CHAR_DEL '<' -#define DEC_HEX_CONVERTER_CHAR_SWAP 's' -#define DEC_HEX_CONVERTER_CHAR_MODE '#' -#define DEC_HEX_CONVERTER_CHAR_OVERFLOW '#' - -#define DEC_HEX_CONVERTER_KEY_FRAME_MARGIN 3 -#define DEC_HEX_CONVERTER_KEY_CHAR_HEIGHT 8 - -#define DEC_HEX_MAX_SUPORTED_DEC_INT 999999999 - -typedef enum { - EventTypeKey, -} EventType; - -typedef struct { - InputEvent input; - EventType type; -} DecHexConverterEvent; - -typedef enum { - ModeDec, - ModeHex, -} Mode; - -// setting up one char array next to the other one causes the canvas_draw_str to display both of them -// when addressing the first one if there's no string terminator or similar indicator. Adding a \0 seems -// to work fine to prevent that, so add a final last char outside the size constants (added on init -// and NEVER changed nor referenced again) -// -// (as a reference, canvas_draw_str ends up calling u8g2_DrawStr from u8g2_font.c, -// that finally ends up calling u8g2_draw_string) -typedef struct { - char dec_number[DEC_HEX_CONVERTER_NUMBER_DIGITS + 1]; - char hex_number[DEC_HEX_CONVERTER_NUMBER_DIGITS + 1]; - Mode mode; // dec / hex - int8_t cursor; // position on keyboard (includes digit letters and other options) - int8_t digit_pos; // current digit on selected mode -} DecHexConverterState; - -// move cursor left / right (TODO: implement menu nav in a more "standard" and reusable way?) -void dec_hex_converter_logic_move_cursor_lr( - DecHexConverterState* const dec_hex_converter_state, - int8_t d) { - dec_hex_converter_state->cursor += d; - - if(dec_hex_converter_state->cursor > DEC_HEX_CONVERTER_KEYS - 1) - dec_hex_converter_state->cursor = 0; - else if(dec_hex_converter_state->cursor < 0) - dec_hex_converter_state->cursor = DEC_HEX_CONVERTER_KEYS - 1; - - // if we're moving left / right to the letters keys on ModeDec just go to the closest available key - if(dec_hex_converter_state->mode == ModeDec) { - if(dec_hex_converter_state->cursor == 10) - dec_hex_converter_state->cursor = 16; - else if(dec_hex_converter_state->cursor == 15) - dec_hex_converter_state->cursor = 9; - } -} - -// move cursor up / down; there're two lines, so we basically toggle -void dec_hex_converter_logic_move_cursor_ud(DecHexConverterState* const dec_hex_converter_state) { - if(dec_hex_converter_state->cursor < 9) { - // move to second line ("down") - dec_hex_converter_state->cursor += 9; - - // if we're reaching the letter keys while ModeDec, just move left / right for the first available key - if(dec_hex_converter_state->mode == ModeDec && - (dec_hex_converter_state->cursor >= 10 && dec_hex_converter_state->cursor <= 15)) { - if(dec_hex_converter_state->cursor <= 12) - dec_hex_converter_state->cursor = 9; - else - dec_hex_converter_state->cursor = 16; - } - } else { - // move to first line ("up") - dec_hex_converter_state->cursor -= 9; - } -} - -// fetch number from current mode and modifies the destination one, RM dnt stel pls -// (if destination is shorter than the output value, overried with "-" chars or something similar) -void dec_hex_converter_logic_convert_number(DecHexConverterState* const dec_hex_converter_state) { - char* s_ptr; - char* d_ptr; - - char dest[DEC_HEX_CONVERTER_NUMBER_DIGITS]; - int i = 0; // current index on destination array - - if(dec_hex_converter_state->mode == ModeDec) { - // DEC to HEX cannot overflow if they're, at least, the same size - - s_ptr = dec_hex_converter_state->dec_number; - d_ptr = dec_hex_converter_state->hex_number; - - int a = atoi(s_ptr); - int r; - while(a != 0) { - r = a % 16; - dest[i] = r + (r < 10 ? '0' : ('A' - 10)); - a /= 16; - i++; - } - - } else { - s_ptr = dec_hex_converter_state->hex_number; - d_ptr = dec_hex_converter_state->dec_number; - - int a = strtol(s_ptr, NULL, 16); - if(a > DEC_HEX_MAX_SUPORTED_DEC_INT) { - // draw all "###" if there's an overflow - for(int j = 0; j < DEC_HEX_CONVERTER_NUMBER_DIGITS; j++) { - d_ptr[j] = DEC_HEX_CONVERTER_CHAR_OVERFLOW; - } - return; - } else { - while(a > 0) { - dest[i++] = (a % 10) + '0'; - a /= 10; - } - } - } - - // dest is reversed, copy to destination pointer and append empty chars at the end - for(int j = 0; j < DEC_HEX_CONVERTER_NUMBER_DIGITS; j++) { - if(i >= 1) - d_ptr[j] = dest[--i]; - else - d_ptr[j] = ' '; - } -} - -// change from DEC to HEX or HEX to DEC, set the digit_pos to the last position not empty on the destination mode -void dec_hex_converter_logic_swap_mode(DecHexConverterState* const dec_hex_converter_state) { - char* n_ptr; - if(dec_hex_converter_state->mode == ModeDec) { - dec_hex_converter_state->mode = ModeHex; - n_ptr = dec_hex_converter_state->hex_number; - } else { - dec_hex_converter_state->mode = ModeDec; - n_ptr = dec_hex_converter_state->dec_number; - } - - dec_hex_converter_state->digit_pos = DEC_HEX_CONVERTER_NUMBER_DIGITS; - for(int i = 0; i < DEC_HEX_CONVERTER_NUMBER_DIGITS; i++) { - if(n_ptr[i] == ' ') { - dec_hex_converter_state->digit_pos = i; - break; - } - } -} - -// removes the number on current digit on current mode -static void - dec_hex_converter_logic_del_number(DecHexConverterState* const dec_hex_converter_state) { - if(dec_hex_converter_state->digit_pos > 0) dec_hex_converter_state->digit_pos--; - - if(dec_hex_converter_state->mode == ModeDec) { - dec_hex_converter_state->dec_number[dec_hex_converter_state->digit_pos] = ' '; - } else { - dec_hex_converter_state->hex_number[dec_hex_converter_state->digit_pos] = ' '; - } -} - -// append a number to the digit on the current mode -static void dec_hex_converter_logic_add_number( - DecHexConverterState* const dec_hex_converter_state, - int8_t number) { - // ignore HEX values on DEC mode (probably button nav will be disabled too, so cannot reach); - // also do not add anything if we're out of bound - if((number > 9 && dec_hex_converter_state->mode == ModeDec) || - dec_hex_converter_state->digit_pos >= DEC_HEX_CONVERTER_NUMBER_DIGITS) - return; - - char* s_ptr; - - if(dec_hex_converter_state->mode == ModeDec) { - s_ptr = dec_hex_converter_state->dec_number; - } else { - s_ptr = dec_hex_converter_state->hex_number; - } - - if(number < 10) { - s_ptr[dec_hex_converter_state->digit_pos] = number + '0'; - } else { - s_ptr[dec_hex_converter_state->digit_pos] = (number - 10) + 'A'; // A-F (HEX only) - } - - dec_hex_converter_state->digit_pos++; -} - -// --------------- - -static void dec_hex_converter_render_callback(Canvas* const canvas, void* ctx) { - const DecHexConverterState* dec_hex_converter_state = acquire_mutex((ValueMutex*)ctx, 25); - if(dec_hex_converter_state == NULL) { - return; - } - - canvas_set_color(canvas, ColorBlack); - - // DEC - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 2, 10, "DEC: "); - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 2 + 30, 10, dec_hex_converter_state->dec_number); - - // HEX - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 2, 10 + 12, "HEX: "); - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 2 + 30, 10 + 12, dec_hex_converter_state->hex_number); - - // current mode indicator - // char buffer[4]; - // snprintf(buffer, sizeof(buffer), "%u", dec_hex_converter_state->digit_pos); // debug: show digit position instead of selected mode - if(dec_hex_converter_state->mode == ModeDec) { - canvas_draw_glyph(canvas, 128 - 10, 10, DEC_HEX_CONVERTER_CHAR_MODE); - } else { - canvas_draw_glyph(canvas, 128 - 10, 10 + 12, DEC_HEX_CONVERTER_CHAR_MODE); - } - - // draw the line - canvas_draw_line(canvas, 2, 25, 128 - 3, 25); - - // draw the keyboard - uint8_t _x = 5; - uint8_t _y = 25 + 15; // line + 10 - - for(int i = 0; i < DEC_HEX_CONVERTER_KEYS; i++) { - char g; - if(i < 10) - g = (i + '0'); - else if(i < 16) - g = ((i - 10) + 'A'); - else if(i == 16) - g = DEC_HEX_CONVERTER_CHAR_DEL; // '<' - else - g = DEC_HEX_CONVERTER_CHAR_SWAP; // 's' - - uint8_t g_w = canvas_glyph_width(canvas, g); - - // disable letters on DEC mode (but keep the previous width for visual purposes - show "blank keys") - if(dec_hex_converter_state->mode == ModeDec && i > 9 && i < 16) g = ' '; - - if(dec_hex_converter_state->cursor == i) { - canvas_draw_box( - canvas, - _x - DEC_HEX_CONVERTER_KEY_FRAME_MARGIN, - _y - (DEC_HEX_CONVERTER_KEY_CHAR_HEIGHT + DEC_HEX_CONVERTER_KEY_FRAME_MARGIN), - DEC_HEX_CONVERTER_KEY_FRAME_MARGIN + g_w + DEC_HEX_CONVERTER_KEY_FRAME_MARGIN, - DEC_HEX_CONVERTER_KEY_CHAR_HEIGHT + DEC_HEX_CONVERTER_KEY_FRAME_MARGIN * 2); - canvas_set_color(canvas, ColorWhite); - canvas_draw_glyph(canvas, _x, _y, g); - canvas_set_color(canvas, ColorBlack); - } else { - canvas_draw_frame( - canvas, - _x - DEC_HEX_CONVERTER_KEY_FRAME_MARGIN, - _y - (DEC_HEX_CONVERTER_KEY_CHAR_HEIGHT + DEC_HEX_CONVERTER_KEY_FRAME_MARGIN), - DEC_HEX_CONVERTER_KEY_FRAME_MARGIN + g_w + DEC_HEX_CONVERTER_KEY_FRAME_MARGIN, - DEC_HEX_CONVERTER_KEY_CHAR_HEIGHT + DEC_HEX_CONVERTER_KEY_FRAME_MARGIN * 2); - canvas_draw_glyph(canvas, _x, _y, g); - } - - if(i < 8) { - _x += g_w + DEC_HEX_CONVERTER_KEY_FRAME_MARGIN * 2 + 2; - } else if(i == 8) { - _y += (DEC_HEX_CONVERTER_KEY_CHAR_HEIGHT + DEC_HEX_CONVERTER_KEY_FRAME_MARGIN * 2) + 3; - _x = 7; // some padding at the beginning on second line - } else { - _x += g_w + DEC_HEX_CONVERTER_KEY_FRAME_MARGIN * 2 + 1; - } - } - - release_mutex((ValueMutex*)ctx, dec_hex_converter_state); -} - -static void - dec_hex_converter_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { - furi_assert(event_queue); - - DecHexConverterEvent event = {.type = EventTypeKey, .input = *input_event}; - furi_message_queue_put(event_queue, &event, FuriWaitForever); -} - -static void dec_hex_converter_init(DecHexConverterState* const dec_hex_converter_state) { - dec_hex_converter_state->mode = ModeDec; - dec_hex_converter_state->digit_pos = 0; - - dec_hex_converter_state->dec_number[DEC_HEX_CONVERTER_NUMBER_DIGITS] = '\0'; // null terminator - dec_hex_converter_state->hex_number[DEC_HEX_CONVERTER_NUMBER_DIGITS] = '\0'; // null terminator - - for(int i = 0; i < DEC_HEX_CONVERTER_NUMBER_DIGITS; i++) { - dec_hex_converter_state->dec_number[i] = ' '; - dec_hex_converter_state->hex_number[i] = ' '; - } -} - -// main entry point -int32_t dec_hex_converter_app(void* p) { - UNUSED(p); - - // get event queue - FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(DecHexConverterEvent)); - - // allocate state - DecHexConverterState* dec_hex_converter_state = malloc(sizeof(DecHexConverterState)); - - // set mutex for plugin state (different threads can access it) - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, dec_hex_converter_state, sizeof(dec_hex_converter_state))) { - FURI_LOG_E("DecHexConverter", "cannot create mutex\r\n"); - furi_message_queue_free(event_queue); - free(dec_hex_converter_state); - return 255; - } - - // register callbacks for drawing and input processing - ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, dec_hex_converter_render_callback, &state_mutex); - view_port_input_callback_set(view_port, dec_hex_converter_input_callback, event_queue); - - // open GUI and register view_port - Gui* gui = furi_record_open(RECORD_GUI); - gui_add_view_port(gui, view_port, GuiLayerFullscreen); - - dec_hex_converter_init(dec_hex_converter_state); - - // main loop - DecHexConverterEvent event; - for(bool processing = true; processing;) { - FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - DecHexConverterState* dec_hex_converter_state = - (DecHexConverterState*)acquire_mutex_block(&state_mutex); - - if(event_status == FuriStatusOk) { - // press events - if(event.type == EventTypeKey) { - if(event.input.type == InputTypePress) { - switch(event.input.key) { - default: - break; - case InputKeyUp: - case InputKeyDown: - dec_hex_converter_logic_move_cursor_ud(dec_hex_converter_state); - break; - case InputKeyRight: - dec_hex_converter_logic_move_cursor_lr(dec_hex_converter_state, 1); - break; - case InputKeyLeft: - dec_hex_converter_logic_move_cursor_lr(dec_hex_converter_state, -1); - break; - case InputKeyOk: - if(dec_hex_converter_state->cursor < DEC_HEX_CONVERTER_KEY_DEL) { - // positions from 0 to 15 works as regular numbers (DEC / HEX where applicable) - // (logic won't allow add numbers > 9 on ModeDec) - dec_hex_converter_logic_add_number( - dec_hex_converter_state, dec_hex_converter_state->cursor); - } else if(dec_hex_converter_state->cursor == DEC_HEX_CONVERTER_KEY_DEL) { - // del - dec_hex_converter_logic_del_number(dec_hex_converter_state); - } else { - // swap - dec_hex_converter_logic_swap_mode(dec_hex_converter_state); - } - - dec_hex_converter_logic_convert_number(dec_hex_converter_state); - break; - case InputKeyBack: - processing = false; - break; - } - } - } - } else { - // event timeout - } - - view_port_update(view_port); - release_mutex(&state_mutex, dec_hex_converter_state); - } - - view_port_enabled_set(view_port, false); - gui_remove_view_port(gui, view_port); - furi_record_close(RECORD_GUI); - view_port_free(view_port); - furi_message_queue_free(event_queue); - delete_mutex(&state_mutex); - free(dec_hex_converter_state); - - return 0; -} \ No newline at end of file diff --git a/applications/hid_analyzer/application.fam b/applications/hid_analyzer/application.fam deleted file mode 100644 index 5005190cc..000000000 --- a/applications/hid_analyzer/application.fam +++ /dev/null @@ -1,9 +0,0 @@ -App( - appid="hid_analyzer", - name="HID Analyzer", - apptype=FlipperAppType.PLUGIN, - entry_point="hid_analyzer_app", - cdefines=["APP_HID_ANALYZER"], - stack_size=2 * 1024, - order=40, -) diff --git a/applications/hid_analyzer/helpers/decoder_hid.cpp b/applications/hid_analyzer/helpers/decoder_hid.cpp deleted file mode 100644 index 176bfe0b9..000000000 --- a/applications/hid_analyzer/helpers/decoder_hid.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include "decoder_hid.h" -#include - -constexpr uint32_t clocks_in_us = 64; - -constexpr uint32_t jitter_time_us = 20; -constexpr uint32_t min_time_us = 64; -constexpr uint32_t max_time_us = 80; - -constexpr uint32_t min_time = (min_time_us - jitter_time_us) * clocks_in_us; -constexpr uint32_t mid_time = ((max_time_us - min_time_us) / 2 + min_time_us) * clocks_in_us; -constexpr uint32_t max_time = (max_time_us + jitter_time_us) * clocks_in_us; - -bool DecoderHID::read(uint8_t* data, uint8_t data_size) { - bool result = false; - furi_assert(data_size >= 3); - - if(ready) { - result = true; - hid.decode( - reinterpret_cast(&stored_data), sizeof(uint32_t) * 3, data, data_size); - ready = false; - } - - return result; -} - -void DecoderHID::process_front(bool polarity, uint32_t time) { - if(ready) return; - - if(polarity == true) { - last_pulse_time = time; - } else { - last_pulse_time += time; - - if(last_pulse_time > min_time && last_pulse_time < max_time) { - bool pulse; - - if(last_pulse_time < mid_time) { - // 6 pulses - pulse = false; - } else { - // 5 pulses - pulse = true; - } - - if(last_pulse == pulse) { - pulse_count++; - - if(pulse) { - if(pulse_count > 4) { - pulse_count = 0; - store_data(1); - } - } else { - if(pulse_count > 5) { - pulse_count = 0; - store_data(0); - } - } - } else { - if(last_pulse) { - if(pulse_count > 2) { - store_data(1); - } - } else { - if(pulse_count > 3) { - store_data(0); - } - } - - pulse_count = 0; - last_pulse = pulse; - } - } - } -} - -DecoderHID::DecoderHID() { - reset_state(); -} - -void DecoderHID::store_data(bool data) { - stored_data[0] = (stored_data[0] << 1) | ((stored_data[1] >> 31) & 1); - stored_data[1] = (stored_data[1] << 1) | ((stored_data[2] >> 31) & 1); - stored_data[2] = (stored_data[2] << 1) | data; - - if(hid.can_be_decoded(reinterpret_cast(&stored_data), sizeof(uint32_t) * 3)) { - ready = true; - } -} - -void DecoderHID::reset_state() { - last_pulse = false; - pulse_count = 0; - ready = false; - last_pulse_time = 0; -} diff --git a/applications/hid_analyzer/helpers/decoder_hid.h b/applications/hid_analyzer/helpers/decoder_hid.h deleted file mode 100644 index 5dcb6c265..000000000 --- a/applications/hid_analyzer/helpers/decoder_hid.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include -#include -#include "protocols/protocol_hid.h" - -class DecoderHID { -public: - bool read(uint8_t* data, uint8_t data_size); - void process_front(bool polarity, uint32_t time); - DecoderHID(); - -private: - uint32_t last_pulse_time = 0; - bool last_pulse; - uint8_t pulse_count; - - uint32_t stored_data[3] = {0, 0, 0}; - void store_data(bool data); - - std::atomic ready; - - void reset_state(); - ProtocolHID hid; -}; diff --git a/applications/hid_analyzer/helpers/hid_reader.cpp b/applications/hid_analyzer/helpers/hid_reader.cpp deleted file mode 100644 index 890d7c651..000000000 --- a/applications/hid_analyzer/helpers/hid_reader.cpp +++ /dev/null @@ -1,143 +0,0 @@ -#include "hid_reader.h" -#include -#include -#include - -/** - * @brief private violation assistant for HIDReader - */ -struct HIDReaderAccessor { - static void decode(HIDReader& hid_reader, bool polarity) { - hid_reader.decode(polarity); - } -}; - -void HIDReader::decode(bool polarity) { - uint32_t current_dwt_value = DWT->CYCCNT; - uint32_t period = current_dwt_value - last_dwt_value; - last_dwt_value = current_dwt_value; - - decoder_hid.process_front(polarity, period); - - detect_ticks++; -} - -bool HIDReader::switch_timer_elapsed() { - const uint32_t seconds_to_switch = furi_kernel_get_tick_frequency() * 2.0f; - return (furi_get_tick() - switch_os_tick_last) > seconds_to_switch; -} - -void HIDReader::switch_timer_reset() { - switch_os_tick_last = furi_get_tick(); -} - -void HIDReader::switch_mode() { - switch(type) { - case Type::Normal: - type = Type::Indala; - furi_hal_rfid_change_read_config(62500.0f, 0.25f); - break; - case Type::Indala: - type = Type::Normal; - furi_hal_rfid_change_read_config(125000.0f, 0.5f); - break; - } - - switch_timer_reset(); -} - -static void comparator_trigger_callback(bool level, void* comp_ctx) { - HIDReader* _this = static_cast(comp_ctx); - - HIDReaderAccessor::decode(*_this, !level); -} - -HIDReader::HIDReader() { -} - -void HIDReader::start() { - type = Type::Normal; - - furi_hal_rfid_pins_read(); - furi_hal_rfid_tim_read(125000, 0.5); - furi_hal_rfid_tim_read_start(); - start_comparator(); - - switch_timer_reset(); - last_read_count = 0; -} - -void HIDReader::start_forced(HIDReader::Type _type) { - start(); - if(_type == Type::Indala) { - switch_mode(); - } -} - -void HIDReader::stop() { - furi_hal_rfid_pins_reset(); - furi_hal_rfid_tim_read_stop(); - furi_hal_rfid_tim_reset(); - stop_comparator(); -} - -bool HIDReader::read(LfrfidKeyType* _type, uint8_t* data, uint8_t data_size, bool switch_enable) { - bool result = false; - bool something_read = false; - - if(decoder_hid.read(data, data_size)) { - *_type = LfrfidKeyType::KeyH10301; // should be an OK temp - something_read = true; - } - - // validation - if(something_read) { - switch_timer_reset(); - - if(last_read_type == *_type && memcmp(last_read_data, data, data_size) == 0) { - last_read_count = last_read_count + 1; - - if(last_read_count > 2) { - result = true; - } - } else { - last_read_type = *_type; - memcpy(last_read_data, data, data_size); - last_read_count = 0; - } - } - - // mode switching - if(switch_enable && switch_timer_elapsed()) { - switch_mode(); - last_read_count = 0; - } - - return result; -} - -bool HIDReader::detect() { - bool detected = false; - if(detect_ticks > 10) { - detected = true; - } - detect_ticks = 0; - - return detected; -} - -bool HIDReader::any_read() { - return last_read_count > 0; -} - -void HIDReader::start_comparator(void) { - furi_hal_rfid_comp_set_callback(comparator_trigger_callback, this); - last_dwt_value = DWT->CYCCNT; - - furi_hal_rfid_comp_start(); -} - -void HIDReader::stop_comparator(void) { - furi_hal_rfid_comp_stop(); - furi_hal_rfid_comp_set_callback(NULL, NULL); -} diff --git a/applications/hid_analyzer/helpers/hid_reader.h b/applications/hid_analyzer/helpers/hid_reader.h deleted file mode 100644 index 99dbeb08d..000000000 --- a/applications/hid_analyzer/helpers/hid_reader.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once -#include "decoder_hid.h" -#include "key_info.h" - -//#define RFID_GPIO_DEBUG 1 - -class HIDReader { -public: - enum class Type : uint8_t { - Normal, - Indala, - }; - - HIDReader(); - void start(); - void start_forced(HIDReader::Type type); - void stop(); - bool read(LfrfidKeyType* _type, uint8_t* data, uint8_t data_size, bool switch_enable = true); - - bool detect(); - bool any_read(); - -private: - friend struct HIDReaderAccessor; - - DecoderHID decoder_hid; - - uint32_t last_dwt_value; - - void start_comparator(void); - void stop_comparator(void); - - void decode(bool polarity); - - uint32_t detect_ticks; - - uint32_t switch_os_tick_last; - bool switch_timer_elapsed(); - void switch_timer_reset(); - void switch_mode(); - - LfrfidKeyType last_read_type; - uint8_t last_read_data[LFRFID_KEY_SIZE]; - uint8_t last_read_count; - - Type type = Type::Normal; -}; diff --git a/applications/hid_analyzer/helpers/hid_worker.cpp b/applications/hid_analyzer/helpers/hid_worker.cpp deleted file mode 100644 index 42230858c..000000000 --- a/applications/hid_analyzer/helpers/hid_worker.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "hid_worker.h" - -HIDWorker::HIDWorker() { -} - -HIDWorker::~HIDWorker() { -} - -void HIDWorker::start_read() { - reader.start(); -} - -bool HIDWorker::read() { - static const uint8_t data_size = LFRFID_KEY_SIZE; - uint8_t data[data_size] = {0}; - LfrfidKeyType type; - - bool result = reader.read(&type, data, data_size); - - if(result) { - key.set_type(type); - key.set_data(data, data_size); - }; - - return result; -} - -bool HIDWorker::detect() { - return reader.detect(); -} - -bool HIDWorker::any_read() { - return reader.any_read(); -} - -void HIDWorker::stop_read() { - reader.stop(); -} diff --git a/applications/hid_analyzer/helpers/hid_worker.h b/applications/hid_analyzer/helpers/hid_worker.h deleted file mode 100644 index e6674bd1b..000000000 --- a/applications/hid_analyzer/helpers/hid_worker.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include "key_info.h" -#include "rfid_key.h" -#include "hid_reader.h" - -class HIDWorker { -public: - HIDWorker(); - ~HIDWorker(); - - void start_read(); - bool read(); - bool detect(); - bool any_read(); - void stop_read(); - - RfidKey key; - -private: - HIDReader reader; -}; diff --git a/applications/hid_analyzer/helpers/key_info.h b/applications/hid_analyzer/helpers/key_info.h deleted file mode 100644 index e465011d0..000000000 --- a/applications/hid_analyzer/helpers/key_info.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once -#include - -static const uint8_t LFRFID_KEY_SIZE = 8; -static const uint8_t LFRFID_KEY_NAME_SIZE = 22; - -enum class LfrfidKeyType : uint8_t { - KeyEM4100, - KeyH10301, - KeyI40134, -}; - -const char* lfrfid_key_get_type_string(LfrfidKeyType type); -const char* lfrfid_key_get_manufacturer_string(LfrfidKeyType type); -bool lfrfid_key_get_string_type(const char* string, LfrfidKeyType* type); -uint8_t lfrfid_key_get_type_data_count(LfrfidKeyType type); diff --git a/applications/hid_analyzer/helpers/osc_fsk.h b/applications/hid_analyzer/helpers/osc_fsk.h deleted file mode 100644 index eaaaa10ad..000000000 --- a/applications/hid_analyzer/helpers/osc_fsk.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once -#include - -/** - * This code tries to fit the periods into a given number of cycles (phases) by taking cycles from the next cycle of periods. - */ -class OscFSK { -public: - /** - * Get next period - * @param bit bit value - * @param period return period - * @return bool whether to advance to the next bit - */ - bool next(bool bit, uint16_t* period); - - /** - * FSK ocillator constructor - * - * @param freq_low bit 0 freq - * @param freq_hi bit 1 freq - * @param osc_phase_max max oscillator phase - */ - OscFSK(uint16_t freq_low, uint16_t freq_hi, uint16_t osc_phase_max); - -private: - const uint16_t freq[2]; - const uint16_t osc_phase_max; - int32_t osc_phase_current; -}; diff --git a/applications/hid_analyzer/helpers/protocols/protocol_generic.h b/applications/hid_analyzer/helpers/protocols/protocol_generic.h deleted file mode 100644 index d593f7089..000000000 --- a/applications/hid_analyzer/helpers/protocols/protocol_generic.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once -#include "stdint.h" -#include "stdbool.h" - -class ProtocolGeneric { -public: - /** - * @brief Get the encoded data size - * - * @return uint8_t size of encoded data in bytes - */ - virtual uint8_t get_encoded_data_size() = 0; - - /** - * @brief Get the decoded data size - * - * @return uint8_t size of decoded data in bytes - */ - virtual uint8_t get_decoded_data_size() = 0; - - /** - * @brief encode decoded data - * - * @param decoded_data - * @param decoded_data_size - * @param encoded_data - * @param encoded_data_size - */ - virtual void encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) = 0; - - /** - * @brief decode encoded data - * - * @param encoded_data - * @param encoded_data_size - * @param decoded_data - * @param decoded_data_size - */ - virtual void decode( - const uint8_t* encoded_data, - const uint8_t encoded_data_size, - uint8_t* decoded_data, - const uint8_t decoded_data_size) = 0; - - /** - * @brief fast check that data can be correctly decoded - * - * @param encoded_data - * @param encoded_data_size - * @return true - can be correctly decoded - * @return false - cannot be correctly decoded - */ - virtual bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) = 0; - - virtual ~ProtocolGeneric(){}; -}; diff --git a/applications/hid_analyzer/helpers/protocols/protocol_hid.cpp b/applications/hid_analyzer/helpers/protocols/protocol_hid.cpp deleted file mode 100644 index e60eafa29..000000000 --- a/applications/hid_analyzer/helpers/protocols/protocol_hid.cpp +++ /dev/null @@ -1,155 +0,0 @@ -#include "protocol_hid.h" -#include - -typedef uint32_t HIDCardData; -constexpr uint8_t HIDCount = 3; -constexpr uint8_t HIDBitSize = sizeof(HIDCardData) * 8; - -uint8_t ProtocolHID::get_encoded_data_size() { - return sizeof(HIDCardData) * HIDCount; -} - -uint8_t ProtocolHID::get_decoded_data_size() { - return 3; -} - -void ProtocolHID::encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) { - UNUSED(decoded_data); - UNUSED(decoded_data_size); - UNUSED(encoded_data); - UNUSED(encoded_data_size); - // bob! -} - -void ProtocolHID::decode( - const uint8_t* encoded_data, - const uint8_t encoded_data_size, - uint8_t* decoded_data, - const uint8_t decoded_data_size) { - furi_check(decoded_data_size >= get_decoded_data_size()); - furi_check(encoded_data_size >= get_encoded_data_size()); - - // header check - int16_t second1pos = find_second_1(encoded_data); - - if((*(encoded_data + 1) & 0b1100) != 0x08) { - *decoded_data = 37; - } else { - *decoded_data = (36 - (second1pos - 8)); - } -} - -int16_t ProtocolHID::find_second_1(const uint8_t* encoded_data) { - if((*(encoded_data + 1) & 0b11) == 0b10) { - return 8; - } else { - for(int8_t i = 3; i >= 0; i--) { - if(((*(encoded_data + 0) >> (2 * i)) & 0b11) == 0b10) { - return (12 - i); - } - } - for(int8_t i = 3; i >= 0; i--) { - if(((*(encoded_data + 7) >> (2 * i)) & 0b11) == 0b10) { - return (16 - i); - } - } - for(int8_t i = 3; i >= 2; i--) { - if(((*(encoded_data + 6) >> (2 * i)) & 0b11) == 0b10) { - return (20 - i); - } - } - } - - return -1; -} - -bool ProtocolHID::can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) { - furi_check(encoded_data_size >= get_encoded_data_size()); - - const HIDCardData* card_data = reinterpret_cast(encoded_data); - - // header check - int16_t second1pos = -1; - // packet pre-preamble - if(*(encoded_data + 3) != 0x1D) { - return false; - } - - // packet preamble - if(*(encoded_data + 2) != 0x55) { // first four 0s mandatory in preamble - return false; - } - - if((*(encoded_data + 1) & 0xF0) != 0x50) { // next two 0s mandatory in preamble - return false; - } - - if((*(encoded_data + 1) & 0b1100) != 0x08) { // if it's not a 1... - // either it's a 37-bit or invalid - // so just continue with the manchester encoding checks - } else { // it is a 1. so it could be anywhere between 26 and 36 bit encoding. or invalid. - // we need to find the location of the second 1 - second1pos = find_second_1(encoded_data); - } - - if(second1pos == -1) { - // we're 37 bit or invalid - } - - // data decoding. ensure all is properly manchester encoded - uint32_t result = 0; - - // decode from word 0 - // coded with 01 = 0, 10 = 1 transitions - for(int8_t i = 11; i >= 0; i--) { - switch((*(card_data + 0) >> (2 * i)) & 0b11) { - case 0b01: - result = (result << 1) | 0; - break; - case 0b10: - result = (result << 1) | 1; - break; - default: - return false; - break; - } - } - - // decode from word 1 - // coded with 01 = 0, 10 = 1 transitions - for(int8_t i = 15; i >= 0; i--) { - switch((*(card_data + 1) >> (2 * i)) & 0b11) { - case 0b01: - result = (result << 1) | 0; - break; - case 0b10: - result = (result << 1) | 1; - break; - default: - return false; - break; - } - } - - // decode from word 2 - // coded with 01 = 0, 10 = 1 transitions - for(int8_t i = 15; i >= 0; i--) { - switch((*(card_data + 2) >> (2 * i)) & 0b11) { - case 0b01: - result = (result << 1) | 0; - break; - case 0b10: - result = (result << 1) | 1; - break; - default: - return false; - break; - } - } - - return true; -} diff --git a/applications/hid_analyzer/helpers/protocols/protocol_hid.h b/applications/hid_analyzer/helpers/protocols/protocol_hid.h deleted file mode 100644 index 0e2636d5b..000000000 --- a/applications/hid_analyzer/helpers/protocols/protocol_hid.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#include "protocol_generic.h" - -class ProtocolHID : public ProtocolGeneric { -public: - uint8_t get_encoded_data_size() final; - uint8_t get_decoded_data_size() final; - - void encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) final; - - void decode( - const uint8_t* encoded_data, - const uint8_t encoded_data_size, - uint8_t* decoded_data, - const uint8_t decoded_data_size) final; - - bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final; - -private: - int16_t find_second_1(const uint8_t* encoded_data); -}; diff --git a/applications/hid_analyzer/helpers/pulse_joiner.h b/applications/hid_analyzer/helpers/pulse_joiner.h deleted file mode 100644 index 1639d8371..000000000 --- a/applications/hid_analyzer/helpers/pulse_joiner.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once -#include "stdint.h" - -class PulseJoiner { -public: - /** - * @brief Push timer pulse. First negative pulse is ommited. - * - * @param polarity pulse polarity: true = high2low, false = low2high - * @param period overall period time in timer clicks - * @param pulse pulse time in timer clicks - * - * @return true - next pulse can and must be popped immediatly - */ - bool push_pulse(bool polarity, uint16_t period, uint16_t pulse); - - /** - * @brief Get the next timer pulse. Call only if push_pulse returns true. - * - * @param period overall period time in timer clicks - * @param pulse pulse time in timer clicks - */ - void pop_pulse(uint16_t* period, uint16_t* pulse); - - PulseJoiner(); - -private: - struct Pulse { - bool polarity; - uint16_t time; - }; - - uint8_t pulse_index = 0; - static const uint8_t pulse_max = 6; - Pulse pulses[pulse_max]; -}; diff --git a/applications/hid_analyzer/helpers/rfid_key.h b/applications/hid_analyzer/helpers/rfid_key.h deleted file mode 100644 index 29b87cf9c..000000000 --- a/applications/hid_analyzer/helpers/rfid_key.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -#include "key_info.h" -#include - -class RfidKey { -public: - RfidKey(); - ~RfidKey(); - - void set_type(LfrfidKeyType type); - void set_data(const uint8_t* data, const uint8_t data_size); - void set_name(const char* name); - - LfrfidKeyType get_type(); - const uint8_t* get_data(); - const char* get_type_text(); - uint8_t get_type_data_count() const; - char* get_name(); - uint8_t get_name_length(); - void clear(); - RfidKey& operator=(const RfidKey& rhs); - -private: - std::array data; - LfrfidKeyType type; - char name[LFRFID_KEY_NAME_SIZE + 1]; -}; diff --git a/applications/hid_analyzer/helpers/rfid_timer_emulator.h b/applications/hid_analyzer/helpers/rfid_timer_emulator.h deleted file mode 100644 index 874a4c3dd..000000000 --- a/applications/hid_analyzer/helpers/rfid_timer_emulator.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -#include -#include "key_info.h" -#include "encoder_generic.h" -#include "encoder_emmarin.h" -#include "encoder_hid_h10301.h" -#include "encoder_indala_40134.h" -#include "pulse_joiner.h" -#include - -class RfidTimerEmulator { -public: - RfidTimerEmulator(); - ~RfidTimerEmulator(); - void start(LfrfidKeyType type, const uint8_t* data, uint8_t data_size); - void stop(); - -private: - EncoderGeneric* current_encoder = nullptr; - - std::map encoders = { - {LfrfidKeyType::KeyEM4100, new EncoderEM()}, - {LfrfidKeyType::KeyH10301, new EncoderHID_H10301()}, - {LfrfidKeyType::KeyI40134, new EncoderIndala_40134()}, - }; - - PulseJoiner pulse_joiner; - static void timer_update_callback(void* ctx); -}; diff --git a/applications/hid_analyzer/helpers/rfid_writer.h b/applications/hid_analyzer/helpers/rfid_writer.h deleted file mode 100644 index 38329877b..000000000 --- a/applications/hid_analyzer/helpers/rfid_writer.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once -#include "stdint.h" - -class RfidWriter { -public: - RfidWriter(); - ~RfidWriter(); - void start(); - void stop(); - void write_em(const uint8_t em_data[5]); - void write_hid(const uint8_t hid_data[3]); - void write_indala(const uint8_t indala_data[3]); - -private: - void write_gap(uint32_t gap_time); - void write_bit(bool value); - void write_byte(uint8_t value); - void write_block(uint8_t page, uint8_t block, bool lock_bit, uint32_t data); - void write_reset(); -}; diff --git a/applications/hid_analyzer/helpers/state_sequencer.h b/applications/hid_analyzer/helpers/state_sequencer.h deleted file mode 100644 index 12512ab51..000000000 --- a/applications/hid_analyzer/helpers/state_sequencer.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#include "stdint.h" -#include -#include - -class TickSequencer { -public: - TickSequencer(); - ~TickSequencer(); - - void tick(); - void reset(); - void clear(); - - void do_every_tick(uint32_t tick_count, std::function fn); - void do_after_tick(uint32_t tick_count, std::function fn); - -private: - std::list > > list; - std::list > >::iterator list_it; - - uint32_t tick_count; - - void do_nothing(); -}; diff --git a/applications/hid_analyzer/hid_analyzer_app.cpp b/applications/hid_analyzer/hid_analyzer_app.cpp deleted file mode 100644 index e8d7fd541..000000000 --- a/applications/hid_analyzer/hid_analyzer_app.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "hid_analyzer_app.h" -#include "scene/hid_analyzer_app_scene_read.h" -#include "scene/hid_analyzer_app_scene_read_success.h" - -HIDApp::HIDApp() - : scene_controller{this} - , notification{"notification"} - , storage{"storage"} - , dialogs{"dialogs"} - , text_store(40) { -} - -HIDApp::~HIDApp() { -} - -void HIDApp::run(void* _args) { - UNUSED(_args); - - view_controller.attach_to_gui(ViewDispatcherTypeFullscreen); - scene_controller.add_scene(SceneType::Read, new HIDAppSceneRead()); - scene_controller.add_scene(SceneType::ReadSuccess, new HIDAppSceneReadSuccess()); - scene_controller.process(100, SceneType::Read); -} \ No newline at end of file diff --git a/applications/hid_analyzer/hid_analyzer_app.h b/applications/hid_analyzer/hid_analyzer_app.h deleted file mode 100644 index 40374663a..000000000 --- a/applications/hid_analyzer/hid_analyzer_app.h +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include "view/container_vm.h" - -#include -#include -#include - -#include "helpers/hid_worker.h" - -class HIDApp { -public: - enum class EventType : uint8_t { - GENERIC_EVENT_ENUM_VALUES, - Next, - MenuSelected, - Stay, - Retry, - }; - - enum class SceneType : uint8_t { - GENERIC_SCENE_ENUM_VALUES, - Read, - ReadSuccess, - }; - - class Event { - public: - union { - int32_t menu_index; - } payload; - - EventType type; - }; - - HIDApp(); - ~HIDApp(); - - void run(void* args); - - // private: - SceneController, HIDApp> scene_controller; - ViewController - view_controller; - RecordController notification; - RecordController storage; - RecordController dialogs; - TextStore text_store; - - HIDWorker worker; -}; \ No newline at end of file diff --git a/applications/hid_analyzer/hid_analyzer_app_launcher.cpp b/applications/hid_analyzer/hid_analyzer_app_launcher.cpp deleted file mode 100644 index 93e210d6c..000000000 --- a/applications/hid_analyzer/hid_analyzer_app_launcher.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "hid_analyzer_app.h" - -// app enter function -extern "C" int32_t hid_analyzer_app(void* args) { - HIDApp* app = new HIDApp(); - app->run(args); - delete app; - - return 0; -} diff --git a/applications/hid_analyzer/scene/hid_analyzer_app_scene_read.cpp b/applications/hid_analyzer/scene/hid_analyzer_app_scene_read.cpp deleted file mode 100644 index 8e7801614..000000000 --- a/applications/hid_analyzer/scene/hid_analyzer_app_scene_read.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "hid_analyzer_app_scene_read.h" -#include - -void HIDAppSceneRead::on_enter(HIDApp* app, bool /* need_restore */) { - auto popup = app->view_controller.get(); - - DOLPHIN_DEED(DolphinDeedRfidRead); - popup->set_header("Searching for\nLF HID RFID", 89, 34, AlignCenter, AlignTop); - popup->set_icon(0, 3, &I_RFIDDolphinReceive_97x61); - - app->view_controller.switch_to(); - app->worker.start_read(); -} - -bool HIDAppSceneRead::on_event(HIDApp* app, HIDApp::Event* event) { - bool consumed = false; - - if(event->type == HIDApp::EventType::Tick) { - if(app->worker.read()) { - DOLPHIN_DEED(DolphinDeedRfidReadSuccess); - notification_message(app->notification, &sequence_success); - app->scene_controller.switch_to_next_scene(HIDApp::SceneType::ReadSuccess); - } else { - if(app->worker.any_read()) { - notification_message(app->notification, &sequence_blink_green_10); - } else if(app->worker.detect()) { - notification_message(app->notification, &sequence_blink_cyan_10); - } else { - notification_message(app->notification, &sequence_blink_cyan_10); - } - } - } - - return consumed; -} - -void HIDAppSceneRead::on_exit(HIDApp* app) { - app->view_controller.get()->clean(); - app->worker.stop_read(); -} diff --git a/applications/hid_analyzer/scene/hid_analyzer_app_scene_read.h b/applications/hid_analyzer/scene/hid_analyzer_app_scene_read.h deleted file mode 100644 index c486f390e..000000000 --- a/applications/hid_analyzer/scene/hid_analyzer_app_scene_read.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../hid_analyzer_app.h" - -class HIDAppSceneRead : public GenericScene { -public: - void on_enter(HIDApp* app, bool need_restore) final; - bool on_event(HIDApp* app, HIDApp::Event* event) final; - void on_exit(HIDApp* app) final; -}; diff --git a/applications/hid_analyzer/scene/hid_analyzer_app_scene_read_success.cpp b/applications/hid_analyzer/scene/hid_analyzer_app_scene_read_success.cpp deleted file mode 100644 index 30bb63939..000000000 --- a/applications/hid_analyzer/scene/hid_analyzer_app_scene_read_success.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include "hid_analyzer_app_scene_read_success.h" -#include "../view/elements/button_element.h" -#include "../view/elements/icon_element.h" -#include "../view/elements/string_element.h" - -void HIDAppSceneReadSuccess::on_enter(HIDApp* app, bool /* need_restore */) { - string_init(string[0]); - string_init(string[1]); - string_init(string[2]); - - auto container = app->view_controller.get(); - - auto button = container->add(); - button->set_type(ButtonElement::Type::Left, "Retry"); - button->set_callback(app, HIDAppSceneReadSuccess::back_callback); - - auto icon = container->add(); - icon->set_icon(3, 12, &I_RFIDBigChip_37x36); - - auto header = container->add(); - header->set_text("HID", 89, 3, 0, AlignCenter); - - // auto line_1_text = container->add(); - auto line_2_text = container->add(); - // auto line_3_text = container->add(); - - // auto line_1_value = container->add(); - auto line_2_value = container->add(); - // auto line_3_value = container->add(); - - const uint8_t* data = app->worker.key.get_data(); - - // line_1_text->set_text("Hi:", 65, 23, 0, AlignRight, AlignBottom, FontSecondary); - line_2_text->set_text("Bit:", 65, 35, 0, AlignRight, AlignBottom, FontSecondary); - // line_3_text->set_text("Bye:", 65, 47, 0, AlignRight, AlignBottom, FontSecondary); - - string_printf(string[1], "%u", data[0]); - - // line_1_value->set_text( - // string_get_cstr(string[0]), 68, 23, 0, AlignLeft, AlignBottom, FontSecondary); - line_2_value->set_text( - string_get_cstr(string[1]), 68, 35, 0, AlignLeft, AlignBottom, FontSecondary); - // line_3_value->set_text( - // string_get_cstr(string[2]), 68, 47, 0, AlignLeft, AlignBottom, FontSecondary); - - app->view_controller.switch_to(); - - notification_message_block(app->notification, &sequence_set_green_255); -} - -bool HIDAppSceneReadSuccess::on_event(HIDApp* app, HIDApp::Event* event) { - bool consumed = false; - - if(event->type == HIDApp::EventType::Retry) { - app->scene_controller.search_and_switch_to_previous_scene({HIDApp::SceneType::Read}); - consumed = true; - } else if(event->type == HIDApp::EventType::Back) { - app->scene_controller.search_and_switch_to_previous_scene({HIDApp::SceneType::Read}); - consumed = true; - } - - return consumed; -} - -void HIDAppSceneReadSuccess::on_exit(HIDApp* app) { - notification_message_block(app->notification, &sequence_reset_green); - app->view_controller.get()->clean(); - string_clear(string[0]); - string_clear(string[1]); - string_clear(string[2]); -} - -void HIDAppSceneReadSuccess::back_callback(void* context) { - HIDApp* app = static_cast(context); - HIDApp::Event event; - event.type = HIDApp::EventType::Retry; - app->view_controller.send_event(&event); -} diff --git a/applications/hid_analyzer/scene/hid_analyzer_app_scene_read_success.h b/applications/hid_analyzer/scene/hid_analyzer_app_scene_read_success.h deleted file mode 100644 index 2668a57bd..000000000 --- a/applications/hid_analyzer/scene/hid_analyzer_app_scene_read_success.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -#include "../hid_analyzer_app.h" - -class HIDAppSceneReadSuccess : public GenericScene { -public: - void on_enter(HIDApp* app, bool need_restore) final; - bool on_event(HIDApp* app, HIDApp::Event* event) final; - void on_exit(HIDApp* app) final; - -private: - static void back_callback(void* context); - - string_t string[3]; -}; diff --git a/applications/hid_analyzer/view/container_vm.h b/applications/hid_analyzer/view/container_vm.h deleted file mode 100644 index 011baa2e9..000000000 --- a/applications/hid_analyzer/view/container_vm.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include - -class ContainerVM : public GenericViewModule { -public: - ContainerVM(); - ~ContainerVM() final; - View* get_view() final; - void clean() final; - - template T* add(); - -private: - View* view; - static void view_draw_callback(Canvas* canvas, void* model); - static bool view_input_callback(InputEvent* event, void* context); -}; diff --git a/applications/hid_analyzer/view/elements/button_element.h b/applications/hid_analyzer/view/elements/button_element.h deleted file mode 100644 index eb9644277..000000000 --- a/applications/hid_analyzer/view/elements/button_element.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once -#include "generic_element.h" - -typedef void (*ButtonElementCallback)(void* context); - -class ButtonElement : public GenericElement { -public: - ButtonElement(); - ~ButtonElement() final; - void draw(Canvas* canvas) final; - bool input(InputEvent* event) final; - - enum class Type : uint8_t { - Left, - Center, - Right, - }; - - void set_type(Type type, const char* text); - void set_callback(void* context, ButtonElementCallback callback); - -private: - Type type = Type::Left; - const char* text = nullptr; - - void* context = nullptr; - ButtonElementCallback callback = nullptr; -}; diff --git a/applications/hid_analyzer/view/elements/generic_element.h b/applications/hid_analyzer/view/elements/generic_element.h deleted file mode 100644 index f5a58b2d9..000000000 --- a/applications/hid_analyzer/view/elements/generic_element.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include -#include - -class GenericElement { -public: - GenericElement(){}; - virtual ~GenericElement(){}; - virtual void draw(Canvas* canvas) = 0; - virtual bool input(InputEvent* event) = 0; - - // TODO that must be accessible only to ContainerVMData - void set_parent_view(View* view); - - // TODO that must be accessible only to inheritors - void lock_model(); - void unlock_model(bool need_redraw); - -private: - View* view = nullptr; -}; diff --git a/applications/hid_analyzer/view/elements/icon_element.h b/applications/hid_analyzer/view/elements/icon_element.h deleted file mode 100644 index a08202741..000000000 --- a/applications/hid_analyzer/view/elements/icon_element.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include "generic_element.h" - -class IconElement : public GenericElement { -public: - IconElement(); - ~IconElement() final; - void draw(Canvas* canvas) final; - bool input(InputEvent* event) final; - - void set_icon(uint8_t x = 0, uint8_t y = 0, const Icon* icon = NULL); - -private: - const Icon* icon = NULL; - uint8_t x = 0; - uint8_t y = 0; -}; diff --git a/applications/hid_analyzer/view/elements/string_element.h b/applications/hid_analyzer/view/elements/string_element.h deleted file mode 100644 index 173fdd601..000000000 --- a/applications/hid_analyzer/view/elements/string_element.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once -#include "generic_element.h" - -class StringElement : public GenericElement { -public: - StringElement(); - ~StringElement() final; - void draw(Canvas* canvas) final; - bool input(InputEvent* event) final; - - void set_text( - const char* text = NULL, - uint8_t x = 0, - uint8_t y = 0, - uint8_t fit_width = 0, - Align horizontal = AlignLeft, - Align vertical = AlignTop, - Font font = FontPrimary); - -private: - const char* text = NULL; - uint8_t x = 0; - uint8_t y = 0; - uint8_t fit_width = 0; - Align horizontal = AlignLeft; - Align vertical = AlignTop; - Font font = FontPrimary; -}; diff --git a/applications/meta/application.fam b/applications/meta/application.fam index fd8b64131..2859d144d 100644 --- a/applications/meta/application.fam +++ b/applications/meta/application.fam @@ -68,7 +68,6 @@ App( apptype=FlipperAppType.METAPACKAGE, provides=[ "picopass", - "hid_analyzer", "barcode_generator", "mouse_jacker", "nrf_sniff", @@ -77,6 +76,6 @@ App( "esp8266_deauth", "wifi_scanner", "wav_player", - "dec_hex_converter", + "multi_converter", ], ) \ No newline at end of file diff --git a/applications/dec_hex_converter/application.fam b/applications/multi_converter/application.fam similarity index 58% rename from applications/dec_hex_converter/application.fam rename to applications/multi_converter/application.fam index 062c40b26..9325a3a35 100644 --- a/applications/dec_hex_converter/application.fam +++ b/applications/multi_converter/application.fam @@ -1,8 +1,8 @@ App( - appid="dec_hex_converter", - name="Dec/Hex Converter", + appid="multi_converter", + name="Multi Converter", apptype=FlipperAppType.PLUGIN, - entry_point="dec_hex_converter_app", + entry_point="multi_converter_app", cdefines=["APP_DEC_HEX_CONVERTER"], requires=["gui"], stack_size=1 * 1024, diff --git a/applications/multi_converter/multi_converter.c b/applications/multi_converter/multi_converter.c new file mode 100644 index 000000000..69b3c71f6 --- /dev/null +++ b/applications/multi_converter/multi_converter.c @@ -0,0 +1,162 @@ +#include +#include +#include +#include + +#include "multi_converter_definitions.h" +#include "multi_converter_mode_display.h" +#include "multi_converter_mode_select.h" + +static void multi_converter_render_callback(Canvas* const canvas, void* ctx) { + + const MultiConverterState* multi_converter_state = acquire_mutex((ValueMutex*)ctx, 25); + if(multi_converter_state == NULL) { + return; + } + + if (multi_converter_state->mode == ModeDisplay) { + multi_converter_mode_display_draw(canvas, multi_converter_state); + } else { + multi_converter_mode_select_draw(canvas, multi_converter_state); + } + + release_mutex((ValueMutex*)ctx, multi_converter_state); +} + +static void multi_converter_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + MultiConverterEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void multi_converter_init(MultiConverterState* const multi_converter_state) { + // initial default values + + multi_converter_state->buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS] = '\0'; + multi_converter_state->buffer_dest[MULTI_CONVERTER_NUMBER_DIGITS] = '\0'; // null terminators + + multi_converter_state->unit_type_orig = UnitTypeDec; + multi_converter_state->unit_type_dest = UnitTypeHex; + + multi_converter_state->keyboard_lock = 0; + + // init the display view + multi_converter_mode_display_reset(multi_converter_state); + + // init the select view + multi_converter_mode_select_reset(multi_converter_state); + + // set ModeDisplay as the current mode + multi_converter_state->mode = ModeDisplay; +} + +// main entry point +int32_t multi_converter_app(void* p) { + UNUSED(p); + + // get event queue + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(MultiConverterEvent)); + + // allocate state + MultiConverterState* multi_converter_state = malloc(sizeof(MultiConverterState)); + + // set mutex for plugin state (different threads can access it) + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, multi_converter_state, sizeof(multi_converter_state))) { + FURI_LOG_E("MultiConverter", "cannot create mutex\r\n"); + free(multi_converter_state); + return 255; + } + + // register callbacks for drawing and input processing + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, multi_converter_render_callback, &state_mutex); + view_port_input_callback_set(view_port, multi_converter_input_callback, event_queue); + + // open GUI and register view_port + Gui* gui = furi_record_open("gui"); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + multi_converter_init(multi_converter_state); + + // main loop + MultiConverterEvent event; + for (bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + MultiConverterState* multi_converter_state = (MultiConverterState*)acquire_mutex_block(&state_mutex); + + if (event_status == FuriStatusOk) { + // press events + if (event.type == EventTypeKey && !multi_converter_state->keyboard_lock) { + if (multi_converter_state->mode == ModeDisplay) { + + if (event.input.key == InputKeyBack) { + if (event.input.type == InputTypePress) processing = false; + } else if (event.input.key == InputKeyOk) { // the "ok" press can be short or long + MultiConverterModeTrigger t = None; + + if (event.input.type == InputTypeLong) t = multi_converter_mode_display_ok(1, multi_converter_state); + else if (event.input.type == InputTypeShort) t = multi_converter_mode_display_ok(0, multi_converter_state); + + if (t == Reset) { + multi_converter_mode_select_reset(multi_converter_state); + multi_converter_state->mode = ModeSelector; + } + } else { + if (event.input.type == InputTypePress) multi_converter_mode_display_navigation(event.input.key, multi_converter_state); + } + + } else { // ModeSelect + if (event.input.type == InputTypePress) { + switch (event.input.key) { + default: + break; + case InputKeyBack: + case InputKeyOk: { + MultiConverterModeTrigger t = multi_converter_mode_select_exit(event.input.key == InputKeyOk ? 1 : 0, multi_converter_state); + + if (t == Reset) { + multi_converter_mode_display_reset(multi_converter_state); + } else if (t == Convert) { + multi_converter_mode_display_convert(multi_converter_state); + } + + multi_converter_state->keyboard_lock = 1; + multi_converter_state->mode = ModeDisplay; + break; + } + case InputKeyLeft: + case InputKeyRight: + multi_converter_mode_select_switch(multi_converter_state); + break; + case InputKeyUp: + multi_converter_mode_select_change_unit(-1, multi_converter_state); + break; + case InputKeyDown: + multi_converter_mode_select_change_unit(1, multi_converter_state); + break; + } + } + } + } else if (multi_converter_state->keyboard_lock) { + multi_converter_state->keyboard_lock = 0; + } + } else { + // event timeout + } + + view_port_update(view_port); + release_mutex(&state_mutex, multi_converter_state); + } + + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close("gui"); + view_port_free(view_port); + furi_message_queue_free(event_queue); + delete_mutex(&state_mutex); + free(multi_converter_state); + + return 0; +} \ No newline at end of file diff --git a/applications/multi_converter/multi_converter_definitions.h b/applications/multi_converter/multi_converter_definitions.h new file mode 100644 index 000000000..fe904342d --- /dev/null +++ b/applications/multi_converter/multi_converter_definitions.h @@ -0,0 +1,82 @@ +#pragma once + +#define MULTI_CONVERTER_NUMBER_DIGITS 9 + +typedef enum { + EventTypeKey, +} EventType; + +typedef struct { + InputEvent input; + EventType type; +} MultiConverterEvent; + +typedef enum { + ModeDisplay, + ModeSelector, +} MultiConverterMode; + +typedef enum { + None, + Reset, + Convert, +} MultiConverterModeTrigger; + +// new units goes here, used as index to the main multi_converter_available_units array (multi_converter_units.h) +typedef enum { + UnitTypeDec, + UnitTypeHex, + UnitTypeBin, + + UnitTypeCelsius, + UnitTypeFahernheit, + UnitTypeKelvin, + + UnitTypeKilometers, + UnitTypeMeters, + UnitTypeCentimeters, + UnitTypeMiles, + UnitTypeFeet, + UnitTypeInches, + + UnitTypeDegree, + UnitTypeRadian, +} MultiConverterUnitType; + +typedef struct { + MultiConverterUnitType selected_unit_type_orig; + MultiConverterUnitType selected_unit_type_dest; + uint8_t select_orig; +} MultiConverterModeSelect; + +typedef struct { + uint8_t cursor; // cursor position when typing + int8_t key; // hover key + uint8_t comma; // comma already added? (only one comma allowed) + uint8_t negative; // is negative? +} MultiConverterModeDisplay; + +typedef struct MultiConverterUnit MultiConverterUnit; +typedef struct MultiConverterState MultiConverterState; + +struct MultiConverterUnit { + uint8_t allow_comma; + uint8_t allow_negative; + uint8_t max_number_keys; + char mini_name[4]; + char name[12]; + void (*convert_function)(MultiConverterState * const); + uint8_t (*allowed_function)(MultiConverterUnitType); +}; + +struct MultiConverterState { + char buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS + 1]; + char buffer_dest[MULTI_CONVERTER_NUMBER_DIGITS + 1]; + MultiConverterUnitType unit_type_orig; + MultiConverterUnitType unit_type_dest; + MultiConverterMode mode; + MultiConverterModeDisplay display; + MultiConverterModeSelect select; + uint8_t keyboard_lock; // used to create a small lock when switching from SELECT to DISPLAY modes + // (debouncing, basically; otherwise it switch modes twice 'cause it's too fast!) +}; diff --git a/applications/multi_converter/multi_converter_mode_display.c b/applications/multi_converter/multi_converter_mode_display.c new file mode 100644 index 000000000..2462cf60c --- /dev/null +++ b/applications/multi_converter/multi_converter_mode_display.c @@ -0,0 +1,284 @@ +#include "multi_converter_mode_display.h" + +#define MULTI_CONVERTER_DISPLAY_KEYS 18 // [0] to [F] + [BACK] + [SELECT] + +#define MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE 0 // long press +#define MULTI_CONVERTER_DISPLAY_KEY_COMMA 1 // long press +#define MULTI_CONVERTER_DISPLAY_KEY_DEL 16 +#define MULTI_CONVERTER_DISPLAY_KEY_SELECT 17 + +#define MULTI_CONVERTER_DISPLAY_CHAR_COMMA '.' +#define MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE '-' +#define MULTI_CONVERTER_DISPLAY_CHAR_DEL '<' +#define MULTI_CONVERTER_DISPLAY_CHAR_SELECT '#' +#define MULTI_CONVERTER_DISPLAY_CHAR_BLANK ' ' + +#define MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN 3 +#define MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT 8 + + +void multi_converter_mode_display_convert(MultiConverterState* const multi_converter_state) { + + // 1.- if origin == destination (in theory user won't be allowed to choose the same options, but it's kinda "valid"...) + // just copy buffer_orig to buffer_dest and that's it + + if (multi_converter_state->unit_type_orig == multi_converter_state->unit_type_dest) { + memcpy(multi_converter_state->buffer_dest, multi_converter_state->buffer_orig, MULTI_CONVERTER_NUMBER_DIGITS); + return; + } + + // 2.- origin_buffer has not null functions + if (multi_converter_get_unit(multi_converter_state->unit_type_orig).convert_function == NULL || multi_converter_get_unit(multi_converter_state->unit_type_orig).allowed_function == NULL) return; + + // 3.- valid destination type (using allowed_destinations function) + if (!multi_converter_get_unit(multi_converter_state->unit_type_orig).allowed_function(multi_converter_state->unit_type_dest)) return; + + multi_converter_get_unit(multi_converter_state->unit_type_orig).convert_function(multi_converter_state); + +} + +void multi_converter_mode_display_draw(Canvas* const canvas, const MultiConverterState* multi_converter_state) { + + canvas_set_color(canvas, ColorBlack); + + // ORIGIN + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 2, 10, multi_converter_get_unit(multi_converter_state->unit_type_orig).mini_name); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 2 + 30, 10, multi_converter_state->buffer_orig); + + // DESTINATION + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 2, 10 + 12, multi_converter_get_unit(multi_converter_state->unit_type_dest).mini_name); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 2 + 30, 10 + 12, multi_converter_state->buffer_dest); + + // SEPARATOR_LINE + canvas_draw_line(canvas, 2, 25, 128 - 3, 25); + + // KEYBOARD + uint8_t _x = 5; + uint8_t _y = 25 + 15; // line + 10 + + for (int i = 0; i < MULTI_CONVERTER_DISPLAY_KEYS; i++) { + + char g; + if (i < 10) g = (i + '0'); + else if (i < 16) g = ((i - 10) + 'A'); + else if (i == MULTI_CONVERTER_DISPLAY_KEY_DEL) g = MULTI_CONVERTER_DISPLAY_CHAR_DEL; + else g = MULTI_CONVERTER_DISPLAY_CHAR_SELECT; + + uint8_t g_w = canvas_glyph_width(canvas, g); + + if (i < 16 && i > multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys-1) { + // some units won't use the full [0] - [F] keyboard, in those situations just hide the char + // (won't be selectable anyway, so no worries here; this is just about drawing stuff) + g = MULTI_CONVERTER_DISPLAY_CHAR_BLANK; + } + + // currently hover key is highlighted + if ((multi_converter_state->display).key == i) { + canvas_draw_box(canvas, + _x - MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN, + _y - (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN), + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN, + MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2 + ); + canvas_set_color(canvas, ColorWhite); + } else { + canvas_draw_frame(canvas, + _x - MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN, + _y - (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN), + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN, + MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2 + ); + } + + // draw key + canvas_draw_glyph(canvas, _x, _y, g); + + // certain keys have long_press features, draw whatever they're using there too + if (i == MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE) { + canvas_draw_box(canvas, + _x + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w - 4, + _y + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN - 2, + 4, + 2 + ); + } else if (i == MULTI_CONVERTER_DISPLAY_KEY_COMMA) { + canvas_draw_box(canvas, + _x + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w - 2, + _y + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN - 2, + 2, + 2 + ); + } + + // back to black + canvas_set_color(canvas, ColorBlack); + + if (i < 8) { + _x += g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2 + 2; + } else if (i == 8) { + _y += (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2) + 3; + _x = 8; // some padding at the beginning on second line + } else { + _x += g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2 + 1; + } + } + +} + +void multi_converter_mode_display_navigation(InputKey key, MultiConverterState* const multi_converter_state) { + + // first move to keyboard position, then check if the ORIGIN allows that specific key, if not jump to the "closest one" + switch (key) { + + default: + break; + + case InputKeyUp: + case InputKeyDown: + if ((multi_converter_state->display).key >= 9) (multi_converter_state->display).key -= 9; + else (multi_converter_state->display).key += 9; + break; + + case InputKeyLeft: + case InputKeyRight: + + (multi_converter_state->display).key += (key == InputKeyLeft ? -1 : 1); + + if ((multi_converter_state->display).key > MULTI_CONVERTER_DISPLAY_KEYS-1) (multi_converter_state->display).key = 0; + else if ((multi_converter_state->display).key < 0) (multi_converter_state->display).key = MULTI_CONVERTER_DISPLAY_KEYS-1; + break; + } + + // if destination key is disabled by max_number_keys, move to the closest one + // (this could be improved with more accurate keys movements, probably...) + if (multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys >= 16) return; // weird, since this means "do not show any number on the keyboard, but just in case..." + + int8_t i = -1; + if (key == InputKeyRight || key == InputKeyDown) i = 1; + + while ((multi_converter_state->display).key < 16 && (multi_converter_state->display).key > multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys-1) { + (multi_converter_state->display).key += i; + if ((multi_converter_state->display).key > MULTI_CONVERTER_DISPLAY_KEYS-1) (multi_converter_state->display).key = 0; + else if ((multi_converter_state->display).key < 0) (multi_converter_state->display).key = MULTI_CONVERTER_DISPLAY_KEYS-1; + } + +} + +void multi_converter_mode_display_reset(MultiConverterState* const multi_converter_state) { + + // clean the buffers + for (int i = 0; i < MULTI_CONVERTER_NUMBER_DIGITS; i++) { + multi_converter_state->buffer_orig[i] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK; + multi_converter_state->buffer_dest[i] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK; + } + + // reset the display flags and index + multi_converter_state->display.cursor = 0; + multi_converter_state->display.key = 0; + multi_converter_state->display.comma = 0; + multi_converter_state->display.negative = 0; +} + +void multi_converter_mode_display_toggle_negative(MultiConverterState* const multi_converter_state) { + if (multi_converter_get_unit(multi_converter_state->unit_type_orig).allow_negative) { + + if (!(multi_converter_state->display).negative) { + // shift origin buffer one to right + add the "-" sign (last digit will be lost) + for (int i = MULTI_CONVERTER_NUMBER_DIGITS-1; i > 0; i--) { + // we could avoid the blanks, but nevermind + multi_converter_state->buffer_orig[i] = multi_converter_state->buffer_orig[i-1]; + } + multi_converter_state->buffer_orig[0] = MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE; + + // only increment cursor if we're not out of bound + if ((multi_converter_state->display).cursor < MULTI_CONVERTER_NUMBER_DIGITS) (multi_converter_state->display).cursor++; + } else { + // shift origin buffer one to left, append ' ' on the end + for (int i = 0; i < MULTI_CONVERTER_NUMBER_DIGITS-1; i++) { + if (multi_converter_state->buffer_orig[i] == MULTI_CONVERTER_DISPLAY_CHAR_BLANK) break; + + multi_converter_state->buffer_orig[i] = multi_converter_state->buffer_orig[i+1]; + } + multi_converter_state->buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS-1] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK; + + (multi_converter_state->display).cursor--; + } + + // toggle flag + (multi_converter_state->display).negative ^= 1; + } +} + +void multi_converter_mode_display_add_comma(MultiConverterState* const multi_converter_state) { + if ( + !multi_converter_get_unit(multi_converter_state->unit_type_orig).allow_comma || + (multi_converter_state->display).comma || + !(multi_converter_state->display).cursor || + ((multi_converter_state->display).cursor == (MULTI_CONVERTER_NUMBER_DIGITS - 1)) + ) return; // maybe not allowerd; or one comma already in place; also cannot add commas as first or last chars + + // set flag to one + (multi_converter_state->display).comma = 1; + + multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = MULTI_CONVERTER_DISPLAY_CHAR_COMMA; + (multi_converter_state->display).cursor++; +} + +void multi_converter_mode_display_add_number(MultiConverterState* const multi_converter_state) { + if ((multi_converter_state->display).key > multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys-1) return; + + if ((multi_converter_state->display).key < 10) { + multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = (multi_converter_state->display).key + '0'; + } else { + multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = ((multi_converter_state->display).key - 10) + 'A'; + } + + (multi_converter_state->display).cursor++; +} + +MultiConverterModeTrigger multi_converter_mode_display_ok(uint8_t long_press, MultiConverterState* const multi_converter_state) { + + if ((multi_converter_state->display).key < MULTI_CONVERTER_DISPLAY_KEY_DEL) { + if ((multi_converter_state->display).cursor >= MULTI_CONVERTER_NUMBER_DIGITS) return None; // limit reached, ignore + + // long press on 0 toggle NEGATIVE if allowed, on 1 adds COMMA if allowed + if (long_press) { + + if ((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE) { + // toggle negative + multi_converter_mode_display_toggle_negative(multi_converter_state); + } else if ((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_COMMA) { + // add comma + multi_converter_mode_display_add_comma(multi_converter_state); + } + + } else { + // regular keys + multi_converter_mode_display_add_number(multi_converter_state); + } + + multi_converter_mode_display_convert(multi_converter_state); + + } else if ((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_DEL) { + if ((multi_converter_state->display).cursor > 0) (multi_converter_state->display).cursor--; + + if (multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] == MULTI_CONVERTER_DISPLAY_CHAR_COMMA) (multi_converter_state->display).comma = 0; + if (multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] == MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE) (multi_converter_state->display).negative = 0; + + multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK; + + multi_converter_mode_display_convert(multi_converter_state); + + } else { // MULTI_CONVERTER_DISPLAY_KEY_SELECT + return Reset; + } + + return None; + +} \ No newline at end of file diff --git a/applications/multi_converter/multi_converter_mode_display.h b/applications/multi_converter/multi_converter_mode_display.h new file mode 100644 index 000000000..7c23e230b --- /dev/null +++ b/applications/multi_converter/multi_converter_mode_display.h @@ -0,0 +1,55 @@ +#pragma once + +#include +#include + +#include "multi_converter_definitions.h" +#include "multi_converter_units.h" + +// +// performs a unit conversion from origin to source buffers, if there's any error, overflow or +// non-compatible format (which shouldn't happen, but just in case) abort conversion and outputs +// some "?" strings on the buffer or something similar +// +void multi_converter_mode_display_convert(MultiConverterState* const multi_converter_state); + +// +// draw the main DISPLAY view with the current multi_converter_state values +// +void multi_converter_mode_display_draw(Canvas* const canvas, const MultiConverterState* multi_converter_state); + +// +// keyboard navigation on DISPLAY mode (NAVIGATION only, no BACK nor OK - InputKey guaranteed to be left/right/up/down) +// +void multi_converter_mode_display_navigation(InputKey key, MultiConverterState* const multi_converter_state); + +// +// reset the DISPLAY mode with the current units, cleaning the buffers and different flags; +// call this when exiting the SELECT mode / changing the units +// +void multi_converter_mode_display_reset(MultiConverterState* const multi_converter_state); + +// +// toggle the negative flag on current selected buffer ONLY if the unit allows negative numbers +// (adding negative number may crop the last char on the buffer; it cannot be recovered) +// +void multi_converter_mode_display_toggle_negative(MultiConverterState* const multi_converter_state); + +// +// add a comma/dot/decimal separator/whatever on current selected buffer ONLY if the unit allows it +// (only ONE comma allowed, not in the beginning nor end) +// +void multi_converter_mode_display_add_comma(MultiConverterState* const multi_converter_state); + +// +// add a regular number to the buffer if it's <= the max_number_keys from the unit (not necessary +// since the draw and navigation functions won't allow a trigger for an invalid number, but still +// to keep the "checks" policy on each "add key" function...) +// +void multi_converter_mode_display_add_number(MultiConverterState* const multi_converter_state); + +// +// handle the OK action when selecting a specific key on the keyboard (add a number, a symbol, change mode...) +// returns a ModeTrigger enum value: may or may not let to a mode change on the main loop (WON'T change the mode here) +// +MultiConverterModeTrigger multi_converter_mode_display_ok(uint8_t long_press, MultiConverterState* const multi_converter_state); \ No newline at end of file diff --git a/applications/multi_converter/multi_converter_mode_select.c b/applications/multi_converter/multi_converter_mode_select.c new file mode 100644 index 000000000..c30a361e5 --- /dev/null +++ b/applications/multi_converter/multi_converter_mode_select.c @@ -0,0 +1,160 @@ +#include "multi_converter_mode_select.h" + +#define MULTI_CONVERTER_LIST_ENTRIES_COUNT 3 + +#define MULTI_CONVERTER_INFO_STRING_FROM "FROM:" +#define MULTI_CONVERTER_INFO_STRING_TO "TO:" +#define MULTI_CONVERTER_INFO_STRING_OK "OK: Change" +#define MULTI_CONVERTER_INFO_STRING_BACK "BACK: Cancel" + +void multi_converter_mode_select_draw_destination_offset(uint8_t x, uint8_t y, int8_t d, Canvas* const canvas, const MultiConverterState* multi_converter_state) { + int i = 1; + while (i < MULTI_CONVERTER_AVAILABLE_UNITS) { // in case there's no match, to avoid an endless loop (in theory shouldn't happen, but...) + int ut = multi_converter_get_unit_type_offset((multi_converter_state->select).selected_unit_type_dest, i * d); + if ( + multi_converter_available_units[(multi_converter_state->select).selected_unit_type_orig].allowed_function(ut) && + (multi_converter_state->select).selected_unit_type_orig != ut + ) { + canvas_draw_str(canvas, x, y, multi_converter_available_units[ut].name); + break; + } + i++; + } +} + +void multi_converter_mode_select_draw_selected_unit(uint8_t x, uint8_t y, MultiConverterUnitType unit_type, Canvas* const canvas) { + canvas_draw_box(canvas, x - 2 , y - 10, canvas_string_width(canvas, multi_converter_available_units[unit_type].name) + 4, 13); + canvas_set_color(canvas, ColorWhite); + canvas_draw_str(canvas, x, y, multi_converter_available_units[unit_type].name); + canvas_set_color(canvas, ColorBlack); +} + +void multi_converter_mode_select_draw(Canvas* const canvas, const MultiConverterState* multi_converter_state) { + + int y = 10; + int x = 10; + + canvas_set_color(canvas, ColorBlack); + + // FROM + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, x, y, MULTI_CONVERTER_INFO_STRING_FROM); + + canvas_set_font(canvas, FontSecondary); + + // offset -1 + y += 12; + + canvas_draw_str(canvas, x, y, multi_converter_available_units[ multi_converter_get_unit_type_offset((multi_converter_state->select).selected_unit_type_orig, -1) ].name); + + // current selected element + y += 12; + + multi_converter_mode_select_draw_selected_unit(x, y, (multi_converter_state->select).selected_unit_type_orig, canvas); + + if ((multi_converter_state->select).select_orig) canvas_draw_str(canvas, x - 6, y, ">"); + + // offset +1 + y += 12; + + canvas_draw_str(canvas, x, y, multi_converter_available_units[ multi_converter_get_unit_type_offset((multi_converter_state->select).selected_unit_type_orig, 1) ].name); + + // TO + y = 10; + x = 70; + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, x, y, MULTI_CONVERTER_INFO_STRING_TO); + + canvas_set_font(canvas, FontSecondary); + + // offset -1: go back from current selected destination and find the first one valid (even if it's itself) + y += 12; + + multi_converter_mode_select_draw_destination_offset(x, y, -1, canvas, multi_converter_state); + + // current selected element + y += 12; + + multi_converter_mode_select_draw_selected_unit(x, y, (multi_converter_state->select).selected_unit_type_dest, canvas); + + if (!(multi_converter_state->select).select_orig) canvas_draw_str(canvas, x - 6, y, ">"); + + // offset +1: same but on the opposite direction + y += 12; + + multi_converter_mode_select_draw_destination_offset(x, y, 1, canvas, multi_converter_state); + + // OK / CANCEL + + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 0, 64 - 12, canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_OK) + 4, 12); + canvas_draw_box(canvas, 128 - 4 - canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_BACK), 64 - 12, canvas_string_width(canvas, "BACK: Cancel") + 4, 12); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_str(canvas, 2, 64 - 3, MULTI_CONVERTER_INFO_STRING_OK); + canvas_draw_str(canvas, 128 - 2 - canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_BACK), 64 - 3, MULTI_CONVERTER_INFO_STRING_BACK); +} + +void multi_converter_mode_select_reset(MultiConverterState* const multi_converter_state) { + + // initial pre-selected values are equal to the current selected values + (multi_converter_state->select).selected_unit_type_orig = multi_converter_state->unit_type_orig; + (multi_converter_state->select).selected_unit_type_dest = multi_converter_state->unit_type_dest; + + (multi_converter_state->select).select_orig = 1; +} + +MultiConverterModeTrigger multi_converter_mode_select_exit(uint8_t save_changes, MultiConverterState* const multi_converter_state) { + if (save_changes) { + + multi_converter_state->unit_type_dest = (multi_converter_state->select).selected_unit_type_dest; + + if (multi_converter_state->unit_type_orig == (multi_converter_state->select).selected_unit_type_orig) { + // if the ORIGIN unit didn't changed, just trigger the convert + + return Convert; + } else { + multi_converter_state->unit_type_orig = (multi_converter_state->select).selected_unit_type_orig; + multi_converter_state->unit_type_dest = (multi_converter_state->select).selected_unit_type_dest; + + return Reset; + } + + } + + return None; +} + +void multi_converter_mode_select_switch(MultiConverterState* const multi_converter_state) { + (multi_converter_state->select).select_orig ^= 1; +} + +void multi_converter_mode_select_change_unit(int8_t direction, MultiConverterState* const multi_converter_state) { + + MultiConverterUnitType d; + if ((multi_converter_state->select).select_orig) { + (multi_converter_state->select).selected_unit_type_orig = multi_converter_get_unit_type_offset((multi_converter_state->select).selected_unit_type_orig, direction); + d = (multi_converter_state->select).selected_unit_type_dest; + } else { + d = ((multi_converter_state->select).selected_unit_type_dest + direction) % MULTI_CONVERTER_AVAILABLE_UNITS; + } + + // check each unit with the ORIGIN allowed_function() to make sure we're selecting a valid DESTINATION + // (when changing the ORIGIN unit the DIRECTION in which we'll switch the DESTINATION will be the SAME); + // also notice that ORIGIN must be DIFFERENT than DESTINATION + int i = 0; + while (i < MULTI_CONVERTER_AVAILABLE_UNITS) { + if ( + multi_converter_available_units[(multi_converter_state->select).selected_unit_type_orig].allowed_function(d) && + (multi_converter_state->select).selected_unit_type_orig != d + ) { + (multi_converter_state->select).selected_unit_type_dest = d; + break; + } + + d = multi_converter_get_unit_type_offset(d, direction); + i++; + } + +} diff --git a/applications/multi_converter/multi_converter_mode_select.h b/applications/multi_converter/multi_converter_mode_select.h new file mode 100644 index 000000000..2e4a9ec85 --- /dev/null +++ b/applications/multi_converter/multi_converter_mode_select.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include + +#include "multi_converter_definitions.h" +#include "multi_converter_units.h" + +// +// aux draw function for units offsets and draw stuff +// +void multi_converter_mode_select_draw_destination_offset(uint8_t x, uint8_t y, int8_t d, Canvas* const canvas, const MultiConverterState* multi_converter_state); + +void multi_converter_mode_select_draw_selected_unit(uint8_t x, uint8_t y, MultiConverterUnitType unit_type, Canvas* const canvas); + +// +// draw the main SELECT view with the current multi_converter_state values +// +void multi_converter_mode_select_draw(Canvas* const canvas, const MultiConverterState* multi_converter_state); + +// +// reset the SELECT mode view, showing as "pre-selected" the current working units +// +void multi_converter_mode_select_reset(MultiConverterState* const multi_converter_state); + +// +// exit from SELECT mode and go back to display view, if save_changes == 1 use the current SELECT view info +// to modify the current selected units and reset the views properly (usually if the ORIGIN unit has been +// changed, reset everything; otherwise just trigger the convert function with a new DESTINATION) +// +// currently this function DON'T CHECK invalid unit relations (the navigation and display functions will +// prevent weird behaviours, so for now we're trusting the selected_unit_orig/dest_type values) +// +// returns an enum code MultiConverterDisplayTrigger based on doing nothing (cancel), triggering the display +// convert method or reseting the whole display mode (when fully changing the units) +// +// notice the MODE CHANGE itself is not done here but in the main loop (outside the call) via the ModeTrigger enum element +// +MultiConverterModeTrigger multi_converter_mode_select_exit(uint8_t save_changes, MultiConverterState* const multi_converter_state); + +// +// switch between selecting the ORIGIN or the DESTINATION unit on DISPLAY mode (since there're only +// two options, both left/right arrow keys acts as toggles, no "direction" required) +// +void multi_converter_mode_select_switch(MultiConverterState* const multi_converter_state); + +// +// change the selected unit on SELECTED mode, using the select_orig flag to check if we're switching the +// ORIGIN or the DESTINATION unit; the DIRECTION (up or down to travel the array) is set as a param +// +// when switching the ORIGIN one, reset the DESTINATION to the first valid unit (if the current one is not +// valid anymore); when switching the DESTINATION one, an allowed_function() check is performed in order to +// properly set a valid destination unit. +// +// (notice the draw step also perform which units are valid to display, so no worries about that here) +// +void multi_converter_mode_select_change_unit(int8_t direction, MultiConverterState* const multi_converter_state); diff --git a/applications/multi_converter/multi_converter_units.c b/applications/multi_converter/multi_converter_units.c new file mode 100644 index 000000000..281284923 --- /dev/null +++ b/applications/multi_converter/multi_converter_units.c @@ -0,0 +1,230 @@ +#include "multi_converter_units.h" + +#define MULTI_CONVERTER_CHAR_OVERFLOW '#' +#define MULTI_CONVERTER_MAX_SUPORTED_INT 999999999 + +#define multi_converter_unit_set_overflow(b) for (int _i = 0; _i < MULTI_CONVERTER_NUMBER_DIGITS; _i++) b[_i] = MULTI_CONVERTER_CHAR_OVERFLOW; + +// +// DEC / HEX / BIN conversion +// +void multi_converter_unit_dec_hex_bin_convert(MultiConverterState* const multi_converter_state) { + + char dest[MULTI_CONVERTER_NUMBER_DIGITS]; + + int i = 0; + uint8_t overflow = 0; + + int a = 0; + int r = 0; + uint8_t f = 1; + + switch(multi_converter_state->unit_type_orig) { + default: + break; + case UnitTypeDec: { + a = atoi(multi_converter_state->buffer_orig); + f = (multi_converter_state->unit_type_dest == UnitTypeHex ? 16 : 2); + + break; + } + case UnitTypeHex: + a = strtol(multi_converter_state->buffer_orig, NULL, 16); + f = (multi_converter_state->unit_type_dest == UnitTypeDec ? 10 : 2); + + break; + case UnitTypeBin: + a = strtol(multi_converter_state->buffer_orig, NULL, 2); + f = (multi_converter_state->unit_type_dest == UnitTypeDec ? 10 : 16); + + break; + } + + while (a > 0) { + r = a % f; + dest[i] = r + (r < 10 ? '0' : ('A' - 10) ); + a /= f; + if (i++ >= MULTI_CONVERTER_NUMBER_DIGITS) { + overflow = 1; + break; + } + } + + if (overflow) { + multi_converter_unit_set_overflow(multi_converter_state->buffer_dest); + } else { + // copy DEST (reversed) to destination and append empty chars at the end + for (int j = 0; j < MULTI_CONVERTER_NUMBER_DIGITS; j++) { + if (i >= 1) multi_converter_state->buffer_dest[j] = dest[--i]; + else multi_converter_state->buffer_dest[j] = ' '; + } + } + +} + +uint8_t multi_converter_unit_dec_hex_bin_allowed(MultiConverterUnitType unit_type) { + return (unit_type == UnitTypeDec || unit_type == UnitTypeHex || unit_type == UnitTypeBin); +} + +// +// CEL / FAR / KEL +// +void multi_converter_unit_temperature_convert(MultiConverterState* const multi_converter_state) { + + double a = strtof(multi_converter_state->buffer_orig, NULL); + uint8_t overflow = 0; + + switch(multi_converter_state->unit_type_orig) { + default: + break; + case UnitTypeCelsius: + if (multi_converter_state->unit_type_dest == UnitTypeFahernheit) { + // celsius to fahrenheit + a = (a * ((double) 1.8)) + 32; + } else { // UnitTypeKelvin + a += ((double) 273.15); + } + + break; + case UnitTypeFahernheit: + // fahrenheit to celsius, always + a = (a - 32) / ((double) 1.8); + if (multi_converter_state->unit_type_dest == UnitTypeKelvin) { + // if kelvin, add + a += ((double) 273.15); + } + + break; + case UnitTypeKelvin: + // kelvin to celsius, always + a -= ((double) 273.15); + if (multi_converter_state->unit_type_dest == UnitTypeFahernheit) { + // if fahernheit, convert + a = (a * ((double) 1.8)) + 32; + } + + break; + } + + if (overflow) { + multi_converter_unit_set_overflow(multi_converter_state->buffer_dest); + } else { + + int ret = snprintf(multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%.3lf", a); + + if (ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest); + } + +} + +uint8_t multi_converter_unit_temperature_allowed(MultiConverterUnitType unit_type) { + return (unit_type == UnitTypeCelsius || unit_type == UnitTypeFahernheit || unit_type == UnitTypeKelvin); +} + +// +// KM / M / CM / MILES / FEET / INCHES +// + +void multi_converter_unit_distance_convert(MultiConverterState* const multi_converter_state) { + double a = strtof(multi_converter_state->buffer_orig, NULL); + uint8_t overflow = 0; + + switch(multi_converter_state->unit_type_orig) { + default: + break; + case UnitTypeKilometers: + if (multi_converter_state->unit_type_dest == UnitTypeMeters) a *= ((double) 1000); + else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 100000); + else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.6213711); + else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 3280.839895013); + else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 39370.078740157); + break; + case UnitTypeMeters: + if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a /= ((double) 1000); + else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 100); + else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.0006213711); + else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 3.280839895013); + else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 39.370078740157); + break; + case UnitTypeCentimeters: + if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a /= ((double) 100000); + else if (multi_converter_state->unit_type_dest == UnitTypeMeters) a /= ((double) 100); + else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.000006213711); + else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 0.03280839895013); + else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 0.39370078740157); + break; + + case UnitTypeMiles: + if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a *= ((double) 1.609344); + else if (multi_converter_state->unit_type_dest == UnitTypeMeters) a *= ((double) 1609.344); + else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 160934.4); + else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 5280); + else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 63360); + break; + case UnitTypeFeet: + if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a *= ((double) 0.0003048); + else if (multi_converter_state->unit_type_dest == UnitTypeMeters) a *= ((double) 0.3048); + else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 30.48); + else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.000189393939394); + else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 12); + break; + case UnitTypeInches: + if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a *= ((double) 0.0000254); + else if (multi_converter_state->unit_type_dest == UnitTypeMeters) a *= ((double) 0.0254); + else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 2.54); + else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.0000157828282828); + else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 0.0833333333333); + break; + + } + + if (overflow) { + multi_converter_unit_set_overflow(multi_converter_state->buffer_dest); + } else { + + int ret = snprintf(multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%lf", a); + + if (ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest); + } +} + +uint8_t multi_converter_unit_distance_allowed(MultiConverterUnitType unit_type) { + return ( + unit_type == UnitTypeKilometers || unit_type == UnitTypeMeters || unit_type == UnitTypeCentimeters || + unit_type == UnitTypeMiles || unit_type == UnitTypeFeet || unit_type == UnitTypeInches + ); +} + +// +// DEG / RAD +// + +void multi_converter_unit_angle_convert(MultiConverterState* const multi_converter_state) { + double a = strtof(multi_converter_state->buffer_orig, NULL); + uint8_t overflow = 0; + + switch(multi_converter_state->unit_type_orig) { + default: + break; + case UnitTypeDegree: + if (multi_converter_state->unit_type_dest == UnitTypeRadian) a *= ((double) 0.0174532925199); + break; + + case UnitTypeRadian: + if (multi_converter_state->unit_type_dest == UnitTypeDegree) a *= ((double) 57.2957795131); + break; + } + + if (overflow) { + multi_converter_unit_set_overflow(multi_converter_state->buffer_dest); + } else { + + int ret = snprintf(multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%lf", a); + + if (ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest); + } +} + +uint8_t multi_converter_unit_angle_allowed(MultiConverterUnitType unit_type) { + return (unit_type == UnitTypeDegree || unit_type == UnitTypeRadian); +} \ No newline at end of file diff --git a/applications/multi_converter/multi_converter_units.h b/applications/multi_converter/multi_converter_units.h new file mode 100644 index 000000000..da281627e --- /dev/null +++ b/applications/multi_converter/multi_converter_units.h @@ -0,0 +1,71 @@ +#pragma once + +#include +#include + +#include "multi_converter_definitions.h" + +#define MULTI_CONVERTER_AVAILABLE_UNITS 14 + +#define multi_converter_get_unit(unit_type) multi_converter_available_units[unit_type] +#define multi_converter_get_unit_type_offset(unit_type, offset) (((unit_type + offset) % MULTI_CONVERTER_AVAILABLE_UNITS + MULTI_CONVERTER_AVAILABLE_UNITS) % MULTI_CONVERTER_AVAILABLE_UNITS) +// the modulo operation will fail with extremely large values on the units array + +// DEC / HEX / BIN +void multi_converter_unit_dec_hex_bin_convert(MultiConverterState* const multi_converter_state); +uint8_t multi_converter_unit_dec_hex_bin_allowed(MultiConverterUnitType); + +// CEL / FAR / KEL +void multi_converter_unit_temperature_convert(MultiConverterState* const multi_converter_state); +uint8_t multi_converter_unit_temperature_allowed(MultiConverterUnitType); + +// KM / M / CM / MILES / FEET / INCHES +void multi_converter_unit_distance_convert(MultiConverterState* const multi_converter_state); +uint8_t multi_converter_unit_distance_allowed(MultiConverterUnitType); + +// DEG / RAD +void multi_converter_unit_angle_convert(MultiConverterState* const multi_converter_state); +uint8_t multi_converter_unit_angle_allowed(MultiConverterUnitType unit_type); + +// +// each unit is made of comma? + negative? + keyboard_length + mini_name + name + convert function + allowed function +// (setting functions as NULL will cause convert / select options to be ignored) +// +static const MultiConverterUnit multi_converter_unit_dec = { 0, 0, 10, "DEC\0", "Decimal\0", multi_converter_unit_dec_hex_bin_convert, multi_converter_unit_dec_hex_bin_allowed }; +static const MultiConverterUnit multi_converter_unit_hex = { 0, 0, 16, "HEX\0", "Hexadecimal\0", multi_converter_unit_dec_hex_bin_convert, multi_converter_unit_dec_hex_bin_allowed }; +static const MultiConverterUnit multi_converter_unit_bin = { 0, 0, 2, "BIN\0", "Binary\0", multi_converter_unit_dec_hex_bin_convert, multi_converter_unit_dec_hex_bin_allowed }; + +static const MultiConverterUnit multi_converter_unit_cel = { 1, 1, 10, "CEL\0", "Celsius\0", multi_converter_unit_temperature_convert, multi_converter_unit_temperature_allowed }; +static const MultiConverterUnit multi_converter_unit_far = { 1, 1, 10, "FAR\0", "Fahernheit\0", multi_converter_unit_temperature_convert, multi_converter_unit_temperature_allowed }; +static const MultiConverterUnit multi_converter_unit_kel = { 1, 1, 10, "KEL\0", "Kelvin\0", multi_converter_unit_temperature_convert, multi_converter_unit_temperature_allowed }; + +static const MultiConverterUnit multi_converter_unit_km = { 1, 0, 10, "KM\0", "Kilometers\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed }; +static const MultiConverterUnit multi_converter_unit_m = { 1, 0, 10, "M\0", "Meters\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed }; +static const MultiConverterUnit multi_converter_unit_cm = { 1, 0, 10, "CM\0", "Centimeters\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed }; +static const MultiConverterUnit multi_converter_unit_mi = { 1, 0, 10, "MI\0", "Miles\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed }; +static const MultiConverterUnit multi_converter_unit_ft = { 1, 0, 10, "FT\0", "Feet\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed }; +static const MultiConverterUnit multi_converter_unit_in = { 1, 0, 10, " \"\0", "Inches\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed }; + +static const MultiConverterUnit multi_converter_unit_deg = { 1, 0, 10, "DEG\0", "Degree\0", multi_converter_unit_angle_convert, multi_converter_unit_angle_allowed }; +static const MultiConverterUnit multi_converter_unit_rad = { 1, 0, 10, "RAD\0", "Radian\0", multi_converter_unit_angle_convert, multi_converter_unit_angle_allowed }; + +// index order set by the MultiConverterUnitType enum element (multi_converter_definitions.h) +static const MultiConverterUnit multi_converter_available_units[MULTI_CONVERTER_AVAILABLE_UNITS] = { + [UnitTypeDec] = multi_converter_unit_dec, + [UnitTypeHex] = multi_converter_unit_hex, + [UnitTypeBin] = multi_converter_unit_bin, + + [UnitTypeCelsius] = multi_converter_unit_cel, + [UnitTypeFahernheit] = multi_converter_unit_far, + [UnitTypeKelvin] = multi_converter_unit_kel, + + [UnitTypeKilometers] = multi_converter_unit_km, + [UnitTypeMeters] = multi_converter_unit_m, + [UnitTypeCentimeters] = multi_converter_unit_cm, + [UnitTypeMiles] = multi_converter_unit_mi, + [UnitTypeFeet] = multi_converter_unit_ft, + [UnitTypeInches] = multi_converter_unit_in, + + [UnitTypeDegree] = multi_converter_unit_deg, + [UnitTypeRadian] = multi_converter_unit_rad, +}; \ No newline at end of file diff --git a/documentation/MultiConverter.md b/documentation/MultiConverter.md new file mode 100644 index 000000000..820563622 --- /dev/null +++ b/documentation/MultiConverter.md @@ -0,0 +1,61 @@ +# MultiConverter + +## Author: [theisolinearchip](https://github.com/theisolinearchip/flipperzero_stuff/tree/main/applications/multi_converter) + +An expanded version of my previous __Dec/Hex Converter__, this time allowing more units and a _(probably poorly made from a design-point-of-view)_ selector mode +to swap between different unit groups. + +I wrote it with the idea of _expanding the unit list_ on mind, so adding new ones it's a matter of increasing an array of constants + defining the proper conversion functions. + +(Actually the whole project is more about "making the framework" rather than providing _ALL_ of the possible units : D) + +![Img 1](http://albertgonzalez.coffee/projects/flipperzero/multi_converter/img/1_small.png) ![Img 2](http://albertgonzalez.coffee/projects/flipperzero/multi_converter/img/2_small.png) + +## Current conversions + +- `Decimal / Hexadecimal / Binary` +- `Celsius / Fahernheit / Kelvin` +- `Kilometers / Meters / Centimeters / Miles / Feet / Inches` +- `Degree / Radian` + +## Usage + +Base keyboard allows numbers from `0` to `F`, being disabled (or not) according to the current selected unit. + +Long press on `0` toggles a __negative__ value; long press on `1` sets a __decimal point__ (only if allowed by the current selected unit). + +`<` removes the last character; `#` changes to __Unit Select Mode__. + +### Unit Select Mode + +`Left` and `Right` to swap between __origin unit__ and __destination unit__ (notice the _destination_ will change according to the current selected _origin_). + +`Ok` to save the changes and go back to the __Display Mode__; `Back` to go back without changing any unit. + +## Adding new units + +1. Add the new units in the `MultiConverterUnitType` enum on `multi_converter_definitions.h` (basic definitions header). Notice each enum element will be used as an array index later. + +2. Increase the `MULTI_CONVERTER_AVAILABLE_UNITS` constant on `multi_converter_units.h` (units main header file). + +3. Set a pair of functions for __converting__ units and to __check__ if a target unit is allowed to work with the destination unit (both on `multi_converter_units.h` +and `multi_converter_units.c`; follow the already built-in units for more info). + +4. Add the proper `MultiConverterUnit` structs for each new unit. + +5. Add each new struct to the main `multi_converter_available_units` array. + +And that's it! The system will fetch the new units and display it! + +## Known issues, TODO-list, etc. + +This is an initial release, so expect some bugs and issues (also I don't work with C that much, so there're probably lots of things that can be improved and/or changed!). + +- I've noticed some small decimal variations when "going deep" with some units (like converting __miles__ to __centimeters__ and things like that); probably due to the precision-level required. Need to check that. +- Pending: improve overflow checks. +- The way some long numbers are shown could probably be improved to look fancier. +- Both _origin_ and _destination buffers_ are the same. The destination one could probably be longer in order to avoid certain _overflow scenarios_. +- The GUI needs improvement too: there's a whole __widget/views system__ built in the Flipper that allows things like setting up keys, showing "Save/Back/Cancel" messages with +callbacks and stuff like that. Didn't know anything about them, so I moved on with something more basic (which is probably fince since it's not a "very big project"); but +a more "standard" way with the regular GUI stuff provided by the firmware will be interesting... +- More GUI stuff: the _long click buttons_ for adding a decimal point / negative number aren't very clear on the view itself (I tried to add a small dot / dash symbol, but I think those are small enough to be a little bit confusing)