Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Morse Code FAP #144

Merged
merged 2 commits into from
Nov 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ Also check changelog in releases for latest updates!
- i2c Tools [(By NaejEL)](https://github.com/NaejEL/flipperzero-i2ctools) - C0 -> SCL / C1 -> SDA / GND -> GND | 3v3 logic levels only!
- Temperature Sensor Plugin - HTU21D / SI7021 [(By Mywk)](https://github.com/Mywk/FlipperTemperatureSensor) - [How to Connect](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/applications/plugins/temperature_sensor/Readme.md)
- HC-SR04 Distance sensor - Ported and modified by @xMasterX [(original by Sanqui)](https://github.com/Sanqui/flipperzero-firmware/tree/hc_sr04) - How to connect -> (5V -> VCC) / (GND -> GND) / (13|TX -> Trig) / (14|RX -> Echo)
- Morse Code [(by wh00hw)](https://github.com/wh00hw/MorseCodeFAP)

Games:
- DOOM (fixed) [(By p4nic4ttack)](https://github.com/p4nic4ttack/doom-flipper-zero/)
Expand Down
15 changes: 15 additions & 0 deletions applications/plugins/morse_code/application.fam
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"

)
161 changes: 161 additions & 0 deletions applications/plugins/morse_code/morse_code.c
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.
164 changes: 164 additions & 0 deletions applications/plugins/morse_code/morse_code_worker.c
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");
}
42 changes: 42 additions & 0 deletions applications/plugins/morse_code/morse_code_worker.h
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);