-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #144 from wh00hw/dev
Morse Code FAP
- Loading branch information
Showing
6 changed files
with
383 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
App( | ||
appid="morse_code", | ||
name="Morse Code", | ||
apptype=FlipperAppType.EXTERNAL, | ||
entry_point="morse_code_app", | ||
cdefines=["APP_MORSE_CODE"], | ||
requires=[ | ||
"gui", | ||
], | ||
stack_size=1 * 1024, | ||
order=20, | ||
fap_icon="morse_code_10px.png", | ||
fap_category="Music" | ||
|
||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
#include "morse_code_worker.h" | ||
#include <furi.h> | ||
#include <gui/gui.h> | ||
#include <gui/elements.h> | ||
#include <input/input.h> | ||
#include <stdlib.h> | ||
#include <furi_hal.h> | ||
#include <string.h> | ||
|
||
static const float MORSE_CODE_VOLUMES[] = {0, .25, .5, .75, 1}; | ||
|
||
typedef struct { | ||
FuriString* words; | ||
uint8_t volume; | ||
uint32_t dit_delta; | ||
} MorseCodeModel; | ||
|
||
typedef struct { | ||
MorseCodeModel* model; | ||
FuriMutex** model_mutex; | ||
|
||
FuriMessageQueue* input_queue; | ||
|
||
ViewPort* view_port; | ||
Gui* gui; | ||
|
||
MorseCodeWorker* worker; | ||
} MorseCode; | ||
|
||
|
||
static void render_callback(Canvas* const canvas, void* ctx) { | ||
MorseCode* morse_code = ctx; | ||
furi_check(furi_mutex_acquire(morse_code->model_mutex, FuriWaitForever) == FuriStatusOk); | ||
// border around the edge of the screen | ||
canvas_set_font(canvas, FontPrimary); | ||
|
||
//write words | ||
elements_multiline_text_aligned(canvas, 64, 30, AlignCenter, AlignCenter, furi_string_get_cstr(morse_code->model->words)); | ||
|
||
// volume view_port | ||
uint8_t vol_bar_x_pos = 124; | ||
uint8_t vol_bar_y_pos = 0; | ||
const uint8_t volume_h = | ||
(64 / (COUNT_OF(MORSE_CODE_VOLUMES) - 1)) * morse_code->model->volume; | ||
canvas_draw_frame(canvas, vol_bar_x_pos, vol_bar_y_pos, 4, 64); | ||
canvas_draw_box(canvas, vol_bar_x_pos, vol_bar_y_pos + (64 - volume_h), 4, volume_h); | ||
|
||
//dit bpm | ||
canvas_draw_str_aligned( | ||
canvas, 0, 10, AlignLeft, AlignCenter, furi_string_get_cstr(furi_string_alloc_printf("Dit: %ld ms", morse_code->model->dit_delta))); | ||
|
||
//button info | ||
elements_button_center(canvas, "Press/Hold"); | ||
furi_mutex_release(morse_code->model_mutex); | ||
} | ||
|
||
static void input_callback(InputEvent* input_event, void* ctx) { | ||
MorseCode* morse_code = ctx; | ||
furi_message_queue_put(morse_code->input_queue, input_event, FuriWaitForever); | ||
} | ||
|
||
static void morse_code_worker_callback( | ||
FuriString* words, | ||
void* context) { | ||
MorseCode* morse_code = context; | ||
furi_check(furi_mutex_acquire(morse_code->model_mutex, FuriWaitForever) == FuriStatusOk); | ||
morse_code->model->words = words; | ||
furi_mutex_release(morse_code->model_mutex); | ||
view_port_update(morse_code->view_port); | ||
} | ||
|
||
MorseCode* morse_code_alloc() { | ||
MorseCode* instance = malloc(sizeof(MorseCode)); | ||
|
||
instance->model = malloc(sizeof(MorseCodeModel)); | ||
instance->model->words = furi_string_alloc_set_str(""); | ||
instance->model->volume = 3; | ||
instance->model->dit_delta = 150; | ||
instance->model_mutex = furi_mutex_alloc(FuriMutexTypeNormal); | ||
|
||
instance->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); | ||
|
||
instance->worker = morse_code_worker_alloc(); | ||
|
||
morse_code_worker_set_callback(instance->worker, morse_code_worker_callback, instance); | ||
|
||
instance->view_port = view_port_alloc(); | ||
view_port_draw_callback_set(instance->view_port, render_callback, instance); | ||
view_port_input_callback_set(instance->view_port, input_callback, instance); | ||
|
||
// Open GUI and register view_port | ||
instance->gui = furi_record_open(RECORD_GUI); | ||
gui_add_view_port(instance->gui, instance->view_port, GuiLayerFullscreen); | ||
|
||
return instance; | ||
} | ||
|
||
void morse_code_free(MorseCode* instance) { | ||
gui_remove_view_port(instance->gui, instance->view_port); | ||
furi_record_close(RECORD_GUI); | ||
view_port_free(instance->view_port); | ||
|
||
morse_code_worker_free(instance->worker); | ||
|
||
furi_message_queue_free(instance->input_queue); | ||
|
||
furi_mutex_free(instance->model_mutex); | ||
|
||
free(instance->model); | ||
free(instance); | ||
} | ||
|
||
int32_t morse_code_app() { | ||
MorseCode* morse_code = morse_code_alloc(); | ||
InputEvent input; | ||
morse_code_worker_start(morse_code->worker); | ||
morse_code_worker_set_volume( | ||
morse_code->worker, MORSE_CODE_VOLUMES[morse_code->model->volume]); | ||
morse_code_worker_set_dit_delta(morse_code->worker, morse_code->model->dit_delta); | ||
while(furi_message_queue_get(morse_code->input_queue, &input, FuriWaitForever) == FuriStatusOk){ | ||
furi_check(furi_mutex_acquire(morse_code->model_mutex, FuriWaitForever) == FuriStatusOk); | ||
if(input.key == InputKeyBack) { | ||
furi_mutex_release(morse_code->model_mutex); | ||
break; | ||
}else if(input.key == InputKeyOk){ | ||
if(input.type == InputTypePress) | ||
morse_code_worker_play(morse_code->worker, true); | ||
else if(input.type == InputTypeRelease) | ||
morse_code_worker_play(morse_code->worker, false); | ||
}else if(input.key == InputKeyUp && input.type == InputTypePress){ | ||
if(morse_code->model->volume < COUNT_OF(MORSE_CODE_VOLUMES) - 1) | ||
morse_code->model->volume++; | ||
morse_code_worker_set_volume( | ||
morse_code->worker, MORSE_CODE_VOLUMES[morse_code->model->volume]); | ||
}else if(input.key == InputKeyDown && input.type == InputTypePress){ | ||
if(morse_code->model->volume > 0) | ||
morse_code->model->volume--; | ||
morse_code_worker_set_volume( | ||
morse_code->worker, MORSE_CODE_VOLUMES[morse_code->model->volume]); | ||
}else if(input.key == InputKeyLeft && input.type == InputTypePress){ | ||
if(morse_code->model->dit_delta > 10) | ||
morse_code->model->dit_delta-=10; | ||
morse_code_worker_set_dit_delta( | ||
morse_code->worker, morse_code->model->dit_delta); | ||
} | ||
else if(input.key == InputKeyRight && input.type == InputTypePress){ | ||
if(morse_code->model->dit_delta >= 10) | ||
morse_code->model->dit_delta+=10; | ||
morse_code_worker_set_dit_delta( | ||
morse_code->worker, morse_code->model->dit_delta); | ||
} | ||
|
||
FURI_LOG_D("Input", "%s %s %ld", input_get_key_name(input.key), input_get_type_name(input.type), input.sequence); | ||
|
||
furi_mutex_release(morse_code->model_mutex); | ||
view_port_update(morse_code->view_port); | ||
} | ||
morse_code_worker_stop(morse_code->worker); | ||
morse_code_free(morse_code); | ||
return 0; | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
#include "morse_code_worker.h" | ||
#include <furi_hal.h> | ||
#include <lib/flipper_format/flipper_format.h> | ||
|
||
|
||
#define TAG "MorseCodeWorker" | ||
|
||
#define MORSE_CODE_VERSION 0 | ||
|
||
//A-Z0-1 | ||
const char morse_array[36][6] ={ | ||
".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.", | ||
"--.-", ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--..", ".----", "..---", "...--", "....-", ".....", | ||
"-....", "--...", "---..", "----.", "-----" | ||
}; | ||
const char symbol_array[36] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', | ||
'U', 'V', 'W', 'X', 'Y', 'Z', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}; | ||
|
||
struct MorseCodeWorker { | ||
FuriThread* thread; | ||
MorseCodeWorkerCallback callback; | ||
void* callback_context; | ||
bool is_running; | ||
bool play; | ||
float volume; | ||
uint32_t dit_delta; | ||
FuriString* buffer; | ||
FuriString* words; | ||
}; | ||
|
||
void morse_code_worker_fill_buffer(MorseCodeWorker* instance, uint32_t duration){ | ||
FURI_LOG_D("MorseCode: Duration", "%ld", duration); | ||
if( duration <= instance->dit_delta) | ||
furi_string_push_back(instance->buffer, *DOT); | ||
else if(duration <= (instance->dit_delta * 3)) | ||
furi_string_push_back(instance->buffer, *LINE); | ||
if(furi_string_size(instance->buffer) > 5) | ||
furi_string_reset(instance->buffer); | ||
FURI_LOG_D("MorseCode: Buffer", "%s", furi_string_get_cstr(instance->buffer)); | ||
} | ||
|
||
void morse_code_worker_fill_letter(MorseCodeWorker* instance){ | ||
if(furi_string_size(instance->words) > 63) | ||
furi_string_reset(instance->words); | ||
for (size_t i = 0; i < sizeof(morse_array); i++){ | ||
if(furi_string_cmp_str(instance->buffer, morse_array[i]) == 0){ | ||
furi_string_push_back(instance->words, symbol_array[i]); | ||
furi_string_reset(instance->buffer); | ||
break; | ||
} | ||
} | ||
FURI_LOG_D("MorseCode: Words", "%s", furi_string_get_cstr(instance->words)); | ||
} | ||
|
||
|
||
static int32_t morse_code_worker_thread_callback(void* context) { | ||
furi_assert(context); | ||
MorseCodeWorker* instance = context; | ||
bool was_playing = false; | ||
uint32_t start_tick = 0; | ||
uint32_t end_tick = 0; | ||
bool pushed = true; | ||
bool spaced = true; | ||
while(instance->is_running){ | ||
furi_delay_ms(SLEEP); | ||
if(instance->play){ | ||
if(!was_playing){ | ||
start_tick = furi_get_tick(); | ||
furi_hal_speaker_start(FREQUENCY, instance->volume); | ||
was_playing = true; | ||
} | ||
}else{ | ||
if(was_playing){ | ||
pushed = false; | ||
spaced = false; | ||
furi_hal_speaker_stop(); | ||
end_tick = furi_get_tick(); | ||
was_playing = false; | ||
morse_code_worker_fill_buffer(instance, end_tick - start_tick); | ||
start_tick = 0; | ||
} | ||
} | ||
if(!pushed){ | ||
if(end_tick + (instance->dit_delta * 3) < furi_get_tick()){ | ||
//NEW LETTER | ||
morse_code_worker_fill_letter(instance); | ||
if(instance->callback) | ||
instance->callback(instance->words, instance->callback_context); | ||
pushed = true; | ||
} | ||
} | ||
if(!spaced){ | ||
if(end_tick + (instance->dit_delta * 7) < furi_get_tick()){ | ||
//NEW WORD | ||
furi_string_push_back(instance->words, *SPACE); | ||
if(instance->callback) | ||
instance->callback(instance->words, instance->callback_context); | ||
spaced = true; | ||
} | ||
} | ||
} | ||
return 0; | ||
} | ||
|
||
MorseCodeWorker* morse_code_worker_alloc() { | ||
MorseCodeWorker* instance = malloc(sizeof(MorseCodeWorker)); | ||
instance->thread = furi_thread_alloc(); | ||
furi_thread_set_name(instance->thread, "MorseCodeWorker"); | ||
furi_thread_set_stack_size(instance->thread, 1024); | ||
furi_thread_set_context(instance->thread, instance); | ||
furi_thread_set_callback(instance->thread, morse_code_worker_thread_callback); | ||
instance->play = false; | ||
instance->volume = 1.0f; | ||
instance->dit_delta = 150; | ||
instance->buffer = furi_string_alloc_set_str(""); | ||
instance->words = furi_string_alloc_set_str(""); | ||
return instance; | ||
} | ||
|
||
void morse_code_worker_free(MorseCodeWorker* instance) { | ||
furi_assert(instance); | ||
furi_thread_free(instance->thread); | ||
free(instance); | ||
} | ||
|
||
void morse_code_worker_set_callback( | ||
MorseCodeWorker* instance, | ||
MorseCodeWorkerCallback callback, | ||
void* context) { | ||
furi_assert(instance); | ||
instance->callback = callback; | ||
instance->callback_context = context; | ||
} | ||
|
||
void morse_code_worker_play(MorseCodeWorker* instance, bool play){ | ||
furi_assert(instance); | ||
instance->play = play; | ||
} | ||
|
||
void morse_code_worker_set_volume(MorseCodeWorker* instance, float level){ | ||
furi_assert(instance); | ||
instance->volume = level; | ||
} | ||
|
||
void morse_code_worker_set_dit_delta(MorseCodeWorker* instance, uint32_t delta){ | ||
furi_assert(instance); | ||
instance->dit_delta = delta; | ||
} | ||
|
||
void morse_code_worker_start(MorseCodeWorker* instance) { | ||
furi_assert(instance); | ||
furi_assert(instance->is_running == false); | ||
instance->is_running = true; | ||
furi_thread_start(instance->thread); | ||
FURI_LOG_D("MorseCode: Start", "is Running"); | ||
} | ||
|
||
void morse_code_worker_stop(MorseCodeWorker* instance) { | ||
furi_assert(instance); | ||
furi_assert(instance->is_running == true); | ||
instance->is_running = false; | ||
furi_thread_join(instance->thread); | ||
FURI_LOG_D("MorseCode: Stop", "Stop"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
#pragma once | ||
|
||
#include <stdbool.h> | ||
#include <stdint.h> | ||
#include <furi.h> | ||
|
||
#define FREQUENCY 261.63f | ||
#define SLEEP 10 | ||
#define DOT "." | ||
#define LINE "-" | ||
#define SPACE " " | ||
|
||
typedef void (*MorseCodeWorkerCallback)( | ||
FuriString* buffer, | ||
void* context); | ||
|
||
typedef struct MorseCodeWorker MorseCodeWorker; | ||
|
||
MorseCodeWorker* morse_code_worker_alloc(); | ||
|
||
void morse_code_worker_free(MorseCodeWorker* instance); | ||
|
||
void morse_code_worker_set_callback( | ||
MorseCodeWorker* instance, | ||
MorseCodeWorkerCallback callback, | ||
void* context); | ||
|
||
void morse_code_worker_start(MorseCodeWorker* instance); | ||
|
||
void morse_code_worker_stop(MorseCodeWorker* instance); | ||
|
||
void morse_code_worker_play(MorseCodeWorker* instance, bool play); | ||
|
||
void morse_code_worker_set_volume(MorseCodeWorker* instance, float level); | ||
|
||
void morse_code_worker_set_dit_delta(MorseCodeWorker* instance, uint32_t delta); | ||
|
||
|
||
|
||
|
||
|
||
|