Add MorseCode fap

This commit is contained in:
wh00hw 2022-11-01 11:08:53 +01:00
parent 348c4c3b5f
commit 419a90aa28
6 changed files with 383 additions and 0 deletions

View file

@ -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/)

View file

@ -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"
)

View file

@ -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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

View file

@ -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");
}

View file

@ -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);