Skip to content

Commit 8b9c983

Browse files
author
wh00hw
committed
First commit
0 parents  commit 8b9c983

File tree

4 files changed

+381
-0
lines changed

4 files changed

+381
-0
lines changed

application.fam

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
App(
2+
appid="morse_code",
3+
name="Morse Code",
4+
apptype=FlipperAppType.EXTERNAL,
5+
entry_point="morse_code_app",
6+
cdefines=["APP_MORSE_CODE"],
7+
requires=[
8+
"gui",
9+
],
10+
stack_size=4 * 1024,
11+
order=20,
12+
fap_category="Music"
13+
14+
)

morse_code.c

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
#include "morse_code_worker.h"
2+
#include <furi.h>
3+
#include <gui/gui.h>
4+
#include <gui/elements.h>
5+
#include <input/input.h>
6+
#include <stdlib.h>
7+
#include <furi_hal.h>
8+
#include <string.h>
9+
10+
static const float MORSE_CODE_VOLUMES[] = {0, .25, .5, .75, 1};
11+
12+
typedef struct {
13+
FuriString* words;
14+
uint8_t volume;
15+
uint32_t dit_delta;
16+
} MorseCodeModel;
17+
18+
typedef struct {
19+
MorseCodeModel* model;
20+
FuriMutex** model_mutex;
21+
22+
FuriMessageQueue* input_queue;
23+
24+
ViewPort* view_port;
25+
Gui* gui;
26+
27+
MorseCodeWorker* worker;
28+
} MorseCode;
29+
30+
31+
static void render_callback(Canvas* const canvas, void* ctx) {
32+
MorseCode* morse_code = ctx;
33+
furi_check(furi_mutex_acquire(morse_code->model_mutex, FuriWaitForever) == FuriStatusOk);
34+
// border around the edge of the screen
35+
canvas_set_font(canvas, FontPrimary);
36+
37+
//write words
38+
elements_multiline_text_aligned(canvas, 64, 30, AlignCenter, AlignCenter, furi_string_get_cstr(morse_code->model->words));
39+
40+
// volume view_port
41+
uint8_t vol_bar_x_pos = 124;
42+
uint8_t vol_bar_y_pos = 0;
43+
const uint8_t volume_h =
44+
(64 / (COUNT_OF(MORSE_CODE_VOLUMES) - 1)) * morse_code->model->volume;
45+
canvas_draw_frame(canvas, vol_bar_x_pos, vol_bar_y_pos, 4, 64);
46+
canvas_draw_box(canvas, vol_bar_x_pos, vol_bar_y_pos + (64 - volume_h), 4, volume_h);
47+
48+
//dit bpm
49+
canvas_draw_str_aligned(
50+
canvas, 0, 10, AlignLeft, AlignCenter, furi_string_get_cstr(furi_string_alloc_printf("Dit: %ld ms", morse_code->model->dit_delta)));
51+
52+
//button info
53+
elements_button_center(canvas, "Press/Hold");
54+
furi_mutex_release(morse_code->model_mutex);
55+
}
56+
57+
static void input_callback(InputEvent* input_event, void* ctx) {
58+
MorseCode* morse_code = ctx;
59+
furi_message_queue_put(morse_code->input_queue, input_event, FuriWaitForever);
60+
}
61+
62+
static void morse_code_worker_callback(
63+
FuriString* words,
64+
void* context) {
65+
MorseCode* morse_code = context;
66+
furi_check(furi_mutex_acquire(morse_code->model_mutex, FuriWaitForever) == FuriStatusOk);
67+
morse_code->model->words = words;
68+
furi_mutex_release(morse_code->model_mutex);
69+
view_port_update(morse_code->view_port);
70+
}
71+
72+
MorseCode* morse_code_alloc() {
73+
MorseCode* instance = malloc(sizeof(MorseCode));
74+
75+
instance->model = malloc(sizeof(MorseCodeModel));
76+
instance->model->words = furi_string_alloc_set_str("");
77+
instance->model->volume = 3;
78+
instance->model->dit_delta = 150;
79+
instance->model_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
80+
81+
instance->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
82+
83+
instance->worker = morse_code_worker_alloc();
84+
85+
morse_code_worker_set_callback(instance->worker, morse_code_worker_callback, instance);
86+
87+
instance->view_port = view_port_alloc();
88+
view_port_draw_callback_set(instance->view_port, render_callback, instance);
89+
view_port_input_callback_set(instance->view_port, input_callback, instance);
90+
91+
// Open GUI and register view_port
92+
instance->gui = furi_record_open(RECORD_GUI);
93+
gui_add_view_port(instance->gui, instance->view_port, GuiLayerFullscreen);
94+
95+
return instance;
96+
}
97+
98+
void morse_code_free(MorseCode* instance) {
99+
gui_remove_view_port(instance->gui, instance->view_port);
100+
furi_record_close(RECORD_GUI);
101+
view_port_free(instance->view_port);
102+
103+
morse_code_worker_free(instance->worker);
104+
105+
furi_message_queue_free(instance->input_queue);
106+
107+
furi_mutex_free(instance->model_mutex);
108+
109+
free(instance->model);
110+
free(instance);
111+
}
112+
113+
int32_t morse_code_app() {
114+
MorseCode* morse_code = morse_code_alloc();
115+
InputEvent input;
116+
morse_code_worker_start(morse_code->worker);
117+
morse_code_worker_set_volume(
118+
morse_code->worker, MORSE_CODE_VOLUMES[morse_code->model->volume]);
119+
morse_code_worker_set_dit_delta(morse_code->worker, morse_code->model->dit_delta);
120+
while(furi_message_queue_get(morse_code->input_queue, &input, FuriWaitForever) == FuriStatusOk){
121+
furi_check(furi_mutex_acquire(morse_code->model_mutex, FuriWaitForever) == FuriStatusOk);
122+
if(input.key == InputKeyBack) {
123+
furi_mutex_release(morse_code->model_mutex);
124+
break;
125+
}else if(input.key == InputKeyOk){
126+
if(input.type == InputTypePress)
127+
morse_code_worker_play(morse_code->worker, true);
128+
else if(input.type == InputTypeRelease)
129+
morse_code_worker_play(morse_code->worker, false);
130+
}else if(input.key == InputKeyUp && input.type == InputTypePress){
131+
if(morse_code->model->volume < COUNT_OF(MORSE_CODE_VOLUMES) - 1)
132+
morse_code->model->volume++;
133+
morse_code_worker_set_volume(
134+
morse_code->worker, MORSE_CODE_VOLUMES[morse_code->model->volume]);
135+
}else if(input.key == InputKeyDown && input.type == InputTypePress){
136+
if(morse_code->model->volume > 0)
137+
morse_code->model->volume--;
138+
morse_code_worker_set_volume(
139+
morse_code->worker, MORSE_CODE_VOLUMES[morse_code->model->volume]);
140+
}else if(input.key == InputKeyLeft && input.type == InputTypePress){
141+
if(morse_code->model->dit_delta > 10)
142+
morse_code->model->dit_delta-=10;
143+
morse_code_worker_set_dit_delta(
144+
morse_code->worker, morse_code->model->dit_delta);
145+
}
146+
else if(input.key == InputKeyRight && input.type == InputTypePress){
147+
if(morse_code->model->dit_delta >= 10)
148+
morse_code->model->dit_delta+=10;
149+
morse_code_worker_set_dit_delta(
150+
morse_code->worker, morse_code->model->dit_delta);
151+
}
152+
153+
FURI_LOG_D("Input", "%s %s %ld", input_get_key_name(input.key), input_get_type_name(input.type), input.sequence);
154+
155+
furi_mutex_release(morse_code->model_mutex);
156+
view_port_update(morse_code->view_port);
157+
}
158+
morse_code_worker_stop(morse_code->worker);
159+
morse_code_free(morse_code);
160+
return 0;
161+
}

morse_code_worker.c

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
#include "morse_code_worker.h"
2+
#include <furi_hal.h>
3+
#include <lib/flipper_format/flipper_format.h>
4+
5+
6+
#define TAG "MorseCodeWorker"
7+
8+
#define MORSE_CODE_VERSION 0
9+
10+
//A-Z0-1
11+
const char morse_array[36][6] ={
12+
".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.",
13+
"--.-", ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--..", ".----", "..---", "...--", "....-", ".....",
14+
"-....", "--...", "---..", "----.", "-----"
15+
};
16+
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',
17+
'U', 'V', 'W', 'X', 'Y', 'Z', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'};
18+
19+
struct MorseCodeWorker {
20+
FuriThread* thread;
21+
MorseCodeWorkerCallback callback;
22+
void* callback_context;
23+
bool is_running;
24+
bool play;
25+
float volume;
26+
uint32_t dit_delta;
27+
FuriString* buffer;
28+
FuriString* words;
29+
};
30+
31+
void morse_code_worker_fill_buffer(MorseCodeWorker* instance, uint32_t duration){
32+
FURI_LOG_D("MorseCode: Duration", "%ld", duration);
33+
if( duration <= instance->dit_delta)
34+
furi_string_push_back(instance->buffer, *DOT);
35+
else if(duration <= (instance->dit_delta * 3))
36+
furi_string_push_back(instance->buffer, *LINE);
37+
if(furi_string_size(instance->buffer) > 5)
38+
furi_string_reset(instance->buffer);
39+
FURI_LOG_D("MorseCode: Buffer", "%s", furi_string_get_cstr(instance->buffer));
40+
}
41+
42+
void morse_code_worker_fill_letter(MorseCodeWorker* instance){
43+
if(furi_string_size(instance->words) > 63)
44+
furi_string_reset(instance->words);
45+
for (size_t i = 0; i < sizeof(morse_array); i++){
46+
if(furi_string_cmp_str(instance->buffer, morse_array[i]) == 0){
47+
furi_string_push_back(instance->words, symbol_array[i]);
48+
furi_string_reset(instance->buffer);
49+
break;
50+
}
51+
}
52+
FURI_LOG_D("MorseCode: Words", "%s", furi_string_get_cstr(instance->words));
53+
}
54+
55+
56+
static int32_t morse_code_worker_thread_callback(void* context) {
57+
furi_assert(context);
58+
MorseCodeWorker* instance = context;
59+
bool was_playing = false;
60+
uint32_t start_tick = 0;
61+
uint32_t end_tick = 0;
62+
bool pushed = true;
63+
bool spaced = true;
64+
while(instance->is_running){
65+
furi_delay_ms(SLEEP);
66+
if(instance->play){
67+
if(!was_playing){
68+
start_tick = furi_get_tick();
69+
furi_hal_speaker_start(FREQUENCY, instance->volume);
70+
was_playing = true;
71+
}
72+
}else{
73+
if(was_playing){
74+
pushed = false;
75+
spaced = false;
76+
furi_hal_speaker_stop();
77+
end_tick = furi_get_tick();
78+
was_playing = false;
79+
morse_code_worker_fill_buffer(instance, end_tick - start_tick);
80+
start_tick = 0;
81+
}
82+
}
83+
if(!pushed){
84+
if(end_tick + (instance->dit_delta * 3) < furi_get_tick()){
85+
//NEW LETTER
86+
morse_code_worker_fill_letter(instance);
87+
if(instance->callback)
88+
instance->callback(instance->words, instance->callback_context);
89+
pushed = true;
90+
}
91+
}
92+
if(!spaced){
93+
if(end_tick + (instance->dit_delta * 7) < furi_get_tick()){
94+
//NEW WORD
95+
furi_string_push_back(instance->words, *SPACE);
96+
if(instance->callback)
97+
instance->callback(instance->words, instance->callback_context);
98+
spaced = true;
99+
}
100+
}
101+
}
102+
return 0;
103+
}
104+
105+
MorseCodeWorker* morse_code_worker_alloc() {
106+
MorseCodeWorker* instance = malloc(sizeof(MorseCodeWorker));
107+
instance->thread = furi_thread_alloc();
108+
furi_thread_set_name(instance->thread, "MorseCodeWorker");
109+
furi_thread_set_stack_size(instance->thread, 1024);
110+
furi_thread_set_context(instance->thread, instance);
111+
furi_thread_set_callback(instance->thread, morse_code_worker_thread_callback);
112+
instance->play = false;
113+
instance->volume = 1.0f;
114+
instance->dit_delta = 150;
115+
instance->buffer = furi_string_alloc_set_str("");
116+
instance->words = furi_string_alloc_set_str("");
117+
return instance;
118+
}
119+
120+
void morse_code_worker_free(MorseCodeWorker* instance) {
121+
furi_assert(instance);
122+
furi_thread_free(instance->thread);
123+
free(instance);
124+
}
125+
126+
void morse_code_worker_set_callback(
127+
MorseCodeWorker* instance,
128+
MorseCodeWorkerCallback callback,
129+
void* context) {
130+
furi_assert(instance);
131+
instance->callback = callback;
132+
instance->callback_context = context;
133+
}
134+
135+
void morse_code_worker_play(MorseCodeWorker* instance, bool play){
136+
furi_assert(instance);
137+
instance->play = play;
138+
}
139+
140+
void morse_code_worker_set_volume(MorseCodeWorker* instance, float level){
141+
furi_assert(instance);
142+
instance->volume = level;
143+
}
144+
145+
void morse_code_worker_set_dit_delta(MorseCodeWorker* instance, uint32_t delta){
146+
furi_assert(instance);
147+
instance->dit_delta = delta;
148+
}
149+
150+
void morse_code_worker_start(MorseCodeWorker* instance) {
151+
furi_assert(instance);
152+
furi_assert(instance->is_running == false);
153+
instance->is_running = true;
154+
furi_thread_start(instance->thread);
155+
FURI_LOG_D("MorseCode: Start", "is Running");
156+
}
157+
158+
void morse_code_worker_stop(MorseCodeWorker* instance) {
159+
furi_assert(instance);
160+
furi_assert(instance->is_running == true);
161+
instance->is_running = false;
162+
furi_thread_join(instance->thread);
163+
FURI_LOG_D("MorseCode: Stop", "Stop");
164+
}

morse_code_worker.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#pragma once
2+
3+
#include <stdbool.h>
4+
#include <stdint.h>
5+
#include <furi.h>
6+
7+
#define FREQUENCY 261.63f
8+
#define SLEEP 10
9+
#define DOT "."
10+
#define LINE "-"
11+
#define SPACE " "
12+
13+
typedef void (*MorseCodeWorkerCallback)(
14+
FuriString* buffer,
15+
void* context);
16+
17+
typedef struct MorseCodeWorker MorseCodeWorker;
18+
19+
MorseCodeWorker* morse_code_worker_alloc();
20+
21+
void morse_code_worker_free(MorseCodeWorker* instance);
22+
23+
void morse_code_worker_set_callback(
24+
MorseCodeWorker* instance,
25+
MorseCodeWorkerCallback callback,
26+
void* context);
27+
28+
void morse_code_worker_start(MorseCodeWorker* instance);
29+
30+
void morse_code_worker_stop(MorseCodeWorker* instance);
31+
32+
void morse_code_worker_play(MorseCodeWorker* instance, bool play);
33+
34+
void morse_code_worker_set_volume(MorseCodeWorker* instance, float level);
35+
36+
void morse_code_worker_set_dit_delta(MorseCodeWorker* instance, uint32_t delta);
37+
38+
39+
40+
41+
42+

0 commit comments

Comments
 (0)