diff --git a/infrared_controller.c b/infrared_controller.c index d13500be776..5086b6e75b9 100644 --- a/infrared_controller.c +++ b/infrared_controller.c @@ -187,3 +187,19 @@ bool infrared_controller_receive(InfraredController* controller) { return hit; } + +void infrared_controller_pause(InfraredController* controller) { + if(controller->worker_rx_active) { + FURI_LOG_I(TAG, "Stopping RX worker"); + infrared_worker_rx_stop(controller->worker); + controller->worker_rx_active = false; + } +} + +void infrared_controller_resume(InfraredController* controller) { + if(!controller->worker_rx_active) { + FURI_LOG_I(TAG, "Starting RX worker"); + infrared_worker_rx_start(controller->worker); + controller->worker_rx_active = true; + } +} diff --git a/infrared_controller.h b/infrared_controller.h index 18f3bfa67c7..7ef59077f11 100644 --- a/infrared_controller.h +++ b/infrared_controller.h @@ -10,6 +10,8 @@ void infrared_controller_free(InfraredController* controller); void infrared_controller_set_team(InfraredController* controller, LaserTagTeam team); void infrared_controller_send(InfraredController* controller); bool infrared_controller_receive(InfraredController* controller); +void infrared_controller_pause(InfraredController* controller); +void infrared_controller_resume(InfraredController* controller); #define IR_COMMAND_RED_TEAM 0xA1 #define IR_COMMAND_BLUE_TEAM 0xB2 diff --git a/laser_tag_app.c b/laser_tag_app.c index 4f00ddffa26..1cbd5550d83 100644 --- a/laser_tag_app.c +++ b/laser_tag_app.c @@ -2,6 +2,7 @@ #include "laser_tag_view.h" #include "infrared_controller.h" #include "game_state.h" +#include "lfrfid_reader.h" #include #include #include @@ -20,6 +21,7 @@ struct LaserTagApp { GameState* game_state; LaserTagState state; bool need_redraw; + LFRFIDReader* reader; }; const NotificationSequence sequence_vibro_1 = {&message_vibro_on, &message_vibro_off, NULL}; @@ -144,6 +146,55 @@ static void laser_tag_app_draw_callback(Canvas* canvas, void* context) { FURI_LOG_D(TAG, "Exiting draw callback"); } +static bool matching_team(LaserTagApp* app, uint8_t data) { + if(data == 0) { + return true; + } else if(game_state_get_team(app->game_state) == TeamRed) { + return data == 0xA1; + } else if(game_state_get_team(app->game_state) == TeamBlue) { + return data == 0xB2; + } + return false; +} + +static void tag_callback(uint8_t* data, uint8_t length, void* context) { + LaserTagApp* app = (LaserTagApp*)context; + + if(length != 5) { + FURI_LOG_W(TAG, "Tag is not for game. Length: %d", length); + return; + } + + if(data[0] != 0x13 || data[1] != 0x37) { + FURI_LOG_D( + TAG, + "Tag is not for game. Data: %02x %02x %02x %02x %02x", + data[0], + data[1], + data[2], + data[3], + data[4]); + return; + } + + if(matching_team(app, data[2])) { + if(data[3] == 0xFD) { + uint16_t max_delta_ammo = data[4]; + uint16_t ammo = game_state_get_ammo(app->game_state); + uint16_t delta_ammo = INITIAL_AMMO - ammo; + if(delta_ammo > max_delta_ammo) { + delta_ammo = max_delta_ammo; + } + game_state_increase_ammo(app->game_state, delta_ammo); + FURI_LOG_D(TAG, "Increased ammo by: %d", delta_ammo); + } else { + FURI_LOG_W(TAG, "Tag action unknown: %02x %02x", data[3], data[4]); + } + } else { + FURI_LOG_I(TAG, "Tag not for team: %02x", data[2]); + } +} + LaserTagApp* laser_tag_app_alloc() { FURI_LOG_D(TAG, "Allocating Laser Tag App"); LaserTagApp* app = malloc(sizeof(LaserTagApp)); @@ -186,6 +237,9 @@ LaserTagApp* laser_tag_app_alloc() { } FURI_LOG_I(TAG, "Timer allocated"); + app->reader = lfrfid_reader_alloc(); + lfrfid_reader_set_tag_callback(app->reader, "EM4100", tag_callback, app); + furi_timer_start(app->timer, furi_kernel_get_tick_frequency()); FURI_LOG_D(TAG, "Timer started"); @@ -205,6 +259,10 @@ void laser_tag_app_free(LaserTagApp* app) { if(app->ir_controller) { infrared_controller_free(app->ir_controller); } + if(app->reader) { + lfrfid_reader_free(app->reader); + app->reader = NULL; + } free(app->game_state); furi_record_close(RECORD_GUI); furi_record_close(RECORD_NOTIFICATION); @@ -356,6 +414,27 @@ int32_t laser_tag_app(void* p) { FURI_LOG_I(TAG, "OK key pressed, firing laser"); laser_tag_app_fire(app); break; + case InputKeyUp: + FURI_LOG_I(TAG, "Up key pressed, scanning for ammo"); + notification_message(app->notifications, &sequence_short_beep); + uint16_t ammo = game_state_get_ammo(app->game_state); + infrared_controller_pause(app->ir_controller); + lfrfid_reader_start(app->reader); + for(int i = 0; i < 30; i++) { + furi_delay_ms(100); + if(ammo != game_state_get_ammo(app->game_state)) { + break; + } + } + lfrfid_reader_stop(app->reader); + infrared_controller_resume(app->ir_controller); + if(ammo != game_state_get_ammo(app->game_state)) { + notification_message(app->notifications, &sequence_success); + } else { + notification_message(app->notifications, &sequence_error); + } + app->need_redraw = true; + break; default: break; } diff --git a/lfrfid_reader.c b/lfrfid_reader.c new file mode 100644 index 00000000000..25ba269a747 --- /dev/null +++ b/lfrfid_reader.c @@ -0,0 +1,117 @@ +#include "lfrfid_reader.h" +#include +#include +#include + +#define TAG "LfRfid_Reader" + +typedef enum { + LFRFIDReaderEventTagRead = (1 << 0), + LFRFIDReaderEventStopThread = (1 << 1), + LFRFIDReaderEventAll = (LFRFIDReaderEventTagRead | LFRFIDReaderEventStopThread), +} LFRFIDReaderEventType; + +struct LFRFIDReader { + char* requested_protocol; + ProtocolId protocol; + ProtocolDict* dict; + LFRFIDWorker* worker; + FuriThread* thread; + LFRFIDReaderTagCallback callback; + void* callback_context; +}; + +static void lfrfid_cli_read_callback(LFRFIDWorkerReadResult result, ProtocolId proto, void* ctx) { + furi_assert(ctx); + LFRFIDReader* context = ctx; + if(result == LFRFIDWorkerReadDone) { + context->protocol = proto; + furi_thread_flags_set(furi_thread_get_id(context->thread), LFRFIDReaderEventTagRead); + } +} + +LFRFIDReader* lfrfid_reader_alloc() { + LFRFIDReader* reader = malloc(sizeof(LFRFIDReader)); + reader->protocol = PROTOCOL_NO; + reader->dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + reader->worker = lfrfid_worker_alloc(reader->dict); + + return reader; +} + +void lfrfid_reader_set_tag_callback( + LFRFIDReader* reader, + char* requested_protocol, + LFRFIDReaderTagCallback callback, + void* context) { + furi_assert(reader); + furi_assert(requested_protocol); + reader->requested_protocol = requested_protocol; + reader->callback = callback; + reader->callback_context = context; +} + +static int32_t lfrfid_reader_start_thread(void* ctx) { + LFRFIDReader* reader = (LFRFIDReader*)ctx; + furi_thread_flags_clear(LFRFIDReaderEventAll); + lfrfid_worker_start_thread(reader->worker); + lfrfid_worker_read_start( + reader->worker, LFRFIDWorkerReadTypeASKOnly, lfrfid_cli_read_callback, reader); + while(true) { + uint32_t flags = furi_thread_flags_wait(LFRFIDReaderEventAll, FuriFlagWaitAny, 100); + + if(flags != (unsigned)FuriFlagErrorTimeout) { + if((flags & LFRFIDReaderEventTagRead) == LFRFIDReaderEventTagRead) { + furi_thread_flags_clear(LFRFIDReaderEventTagRead); + if(reader->protocol != PROTOCOL_NO) { + const char* protocol_name = + protocol_dict_get_name(reader->dict, reader->protocol); + if(strcmp(protocol_name, reader->requested_protocol) == 0) { + size_t size = protocol_dict_get_data_size(reader->dict, reader->protocol); + uint8_t* data = malloc(size); + protocol_dict_get_data(reader->dict, reader->protocol, data, size); + if(reader->callback) { + FURI_LOG_D(TAG, "Tag %s detected", protocol_name); + reader->callback(data, size, reader->callback_context); + } else { + FURI_LOG_W(TAG, "No callback set for tag %s", protocol_name); + } + free(data); + } else { + FURI_LOG_W(TAG, "Unsupported tag %s, expected EM4100", protocol_name); + } + } + reader->protocol = PROTOCOL_NO; + lfrfid_worker_read_start( + reader->worker, LFRFIDWorkerReadTypeASKOnly, lfrfid_cli_read_callback, reader); + } else if((flags & LFRFIDReaderEventStopThread) == LFRFIDReaderEventStopThread) { + break; + } + } + } + lfrfid_worker_stop(reader->worker); + lfrfid_worker_stop_thread(reader->worker); + FURI_LOG_D(TAG, "LfRfidReader thread exiting"); + return 0; +} + +void lfrfid_reader_start(LFRFIDReader* reader) { + reader->thread = + furi_thread_alloc_ex("lfrfid_reader", 2048, lfrfid_reader_start_thread, reader); + furi_thread_start(reader->thread); +} + +void lfrfid_reader_stop(LFRFIDReader* reader) { + if(reader->thread) { + furi_thread_flags_set(furi_thread_get_id(reader->thread), LFRFIDReaderEventStopThread); + furi_thread_join(reader->thread); + reader->thread = NULL; + } +} + +void lfrfid_reader_free(LFRFIDReader* reader) { + lfrfid_reader_stop(reader); + protocol_dict_free(reader->dict); + lfrfid_worker_free(reader->worker); + free(reader); +} diff --git a/lfrfid_reader.h b/lfrfid_reader.h new file mode 100644 index 00000000000..48c66c7b85b --- /dev/null +++ b/lfrfid_reader.h @@ -0,0 +1,57 @@ +#pragma once + +/** +* @file lfrfid_reader.h +* @brief EM4100 tag reader, inspired by applications/main/lfrfid/lfrfid_cli.c +* @details This file contains the declaration of the LFRFIDReader structure and its functions. You typically allocate a new LFRFIDReader, set the tag detection callback, start the reader. The tag detection callback is called each time a tag is detected. Once you are done, you stop the reader and free it. +* @author CodeAllNight (MrDerekJamison) +*/ + +#include + +typedef struct LFRFIDReader LFRFIDReader; + +/** + * @brief Callback function for tag detection. + * @param data Tag data. + * @param length Tag data length. + * @param context Callback context. + */ +typedef void (*LFRFIDReaderTagCallback)(uint8_t* data, uint8_t length, void* context); + +/** + * @brief Allocates a new LFRFIDReader. + * @return LFRFIDReader* Pointer to the allocated LFRFIDReader. + */ +LFRFIDReader* lfrfid_reader_alloc(); + +/** + * @brief Sets the tag detection callback. + * @param reader LFRFIDReader to set the callback for. + * @param requested_protocol Requested protocol, e.g. "EM4100". + * @param callback Callback function. + * @param context Callback context. + */ +void lfrfid_reader_set_tag_callback( + LFRFIDReader* reader, + char* requested_protocol, + LFRFIDReaderTagCallback callback, + void* context); + +/** + * @brief Starts the LFRFIDReader. + * @param reader LFRFIDReader to start. + */ +void lfrfid_reader_start(LFRFIDReader* reader); + +/** + * @brief Stops the LFRFIDReader. + * @param reader LFRFIDReader to stop. + */ +void lfrfid_reader_stop(LFRFIDReader* reader); + +/** + * @brief Frees the LFRFIDReader. + * @param reader LFRFIDReader to free. + */ +void lfrfid_reader_free(LFRFIDReader* reader); \ No newline at end of file