Skip to content

Commit

Permalink
views/trade: Cease my abuse of View model
Browse files Browse the repository at this point in the history
After converting select_pokemon, it only makes sense to clean up
trade as well. There were a number of globals vars passed in the
main PokemonFap struct that are only used by trade and have been
moved to the View model data. There are also currently still some
globals vars in the trade code that could either be relocated to
specific functions or moved in to the trade View model.

This also adds a periodic timer to the trade view. The functions
that get called through the lifetime of the trade process are
mostly in an interrupt context. This means we cannot request a
View update via with_view_model(). In order to handle regular
drawing, a timer is used to call a function out of an interrupt
context that does a null with_view_model() to simply trigger a
redraw.
  • Loading branch information
kbembedded committed Sep 24, 2023
1 parent 5ce0ed1 commit 5884a74
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 49 deletions.
8 changes: 0 additions & 8 deletions pokemon_app.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,6 @@ struct pokemon_fap {
/* The currently selected pokemon */
int curr_pokemon;

/* Some state tracking */
/* This, combined with some globals in trade.cpp, can probably be better
* consolidated at some point.
*/
bool trading;
bool connected;
render_gameboy_state_t gameboy_status;

/* TODO: Other variables will end up here, like selected level, EV/IV,
* moveset, etc. Likely will want to be another sub struct similar to
* the actual pokemon data structure.
Expand Down
143 changes: 102 additions & 41 deletions views/trade.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#include <furi_hal_light.h>
#include <furi.h>

#include <gui/view.h>
#include <pokemon_icons.h>

Expand Down Expand Up @@ -31,6 +33,15 @@

#define TRADE_CENTRE_WAIT 0xFD

struct trade_model {
bool trading;
bool connected;
render_gameboy_state_t gameboy_status;
uint8_t curr_pokemon;
const PokemonTable* pokemon_table;
FuriTimer* draw_timer;
};

typedef unsigned char byte;
typedef enum { NOT_CONNECTED, CONNECTED, TRADE_CENTRE, COLOSSEUM } connection_state_t;
typedef enum {
Expand All @@ -49,15 +60,17 @@ typedef enum {
} trade_centre_state_t;

/* TODO: Convert all of these to be maintained in a struct in the Trade context */
uint8_t out_data = 0;
uint8_t in_data = 0;
uint8_t shift = 0;
uint32_t time = 0;
volatile int counter = 0;
volatile bool procesing = true;
volatile connection_state_t connection_state = NOT_CONNECTED;
volatile trade_centre_state_t trade_centre_state = INIT;
unsigned char INPUT_BLOCK[405];
uint8_t out_data = 0; // Should be able to be made as part of view model or static in used function
uint8_t in_data = 0; //Should be able to be made as part of view model, is used in multiple funcs
uint8_t shift = 0; //Should be able to be made as part of view model, is used in multiple funcs
uint32_t time = 0; //Should be able to be made static in used function
volatile int counter = 0; // Should be able to be made static in used function
volatile bool procesing = true; // Review this vars use, it could potentially be removed
volatile connection_state_t connection_state =
NOT_CONNECTED; // Should be made in to view model struct
volatile trade_centre_state_t trade_centre_state =
INIT; // Should be able to be made part of view model
unsigned char INPUT_BLOCK[405]; // Put this in pokemon_fap? Not sure yet

void screen_gameboy_connect(Canvas* const canvas) {
canvas_draw_frame(canvas, 0, 0, 128, 64);
Expand All @@ -80,11 +93,12 @@ int time_in_seconds = 0;

static void trade_draw_callback(Canvas* canvas, void* model) {
const char* gameboy_status_text = NULL;
PokemonFap* pokemon_fap = *(PokemonFap**)model;
struct trade_model* view_model = model;
uint8_t curr_pokemon = view_model->curr_pokemon;

canvas_clear(canvas);
if(!pokemon_fap->trading) {
if(!pokemon_fap->connected) {
if(!view_model->trading) {
if(!view_model->connected) {
furi_hal_light_set(LightGreen, 0x00);
furi_hal_light_set(LightBlue, 0x00);
furi_hal_light_set(LightRed, 0xff);
Expand All @@ -96,7 +110,7 @@ static void trade_draw_callback(Canvas* canvas, void* model) {
screen_gameboy_connected(canvas);
}
} else {
switch(pokemon_fap->gameboy_status) {
switch(view_model->gameboy_status) {
case GAMEBOY_TRADING:
furi_hal_light_set(LightGreen, 0x00);
furi_hal_light_set(LightRed, 0x00);
Expand All @@ -111,8 +125,7 @@ static void trade_draw_callback(Canvas* canvas, void* model) {
case GAMEBOY_READY:
case GAMEBOY_WAITING:
case GAMEBOY_SEND:
canvas_draw_icon(
canvas, 38, 11, pokemon_fap->pokemon_table[pokemon_fap->curr_pokemon].icon);
canvas_draw_icon(canvas, 38, 11, view_model->pokemon_table[curr_pokemon].icon);
break;
default:
// Default state added to eliminated enum warning
Expand All @@ -123,7 +136,7 @@ static void trade_draw_callback(Canvas* canvas, void* model) {
canvas_draw_frame(canvas, 0, 0, 128, 64);
canvas_draw_icon(canvas, 24, 0, &I_Space_80x18);

switch(pokemon_fap->gameboy_status) {
switch(view_model->gameboy_status) {
case GAMEBOY_READY:
gameboy_status_text = "READY";
break;
Expand Down Expand Up @@ -217,10 +230,17 @@ byte getMenuResponse(byte in) {
byte getTradeCentreResponse(byte in, void* context) {
PokemonFap* pokemon_fap = (PokemonFap*)context;
uint8_t* trade_block_flat = (uint8_t*)pokemon_fap->trade_block;
render_gameboy_state_t gameboy_status;
byte send = in;

furi_assert(context);

with_view_model(
pokemon_fap->trade_view,
struct trade_model * model,
{ gameboy_status = model->gameboy_status; },
false);

switch(trade_centre_state) {
case INIT:
// TODO: What does this value of in mean?
Expand All @@ -229,7 +249,7 @@ byte getTradeCentreResponse(byte in, void* context) {
if(counter == 5) {
trade_centre_state = READY_TO_GO;
// CLICK EN LA MESA
pokemon_fap->gameboy_status = GAMEBOY_READY;
gameboy_status = GAMEBOY_READY;
}
counter++;
}
Expand All @@ -250,7 +270,7 @@ byte getTradeCentreResponse(byte in, void* context) {
if((in & 0xF0) == 0xF0) {
if(counter == 5) {
trade_centre_state = WAITING_TO_SEND_DATA;
pokemon_fap->gameboy_status = GAMEBOY_WAITING;
gameboy_status = GAMEBOY_WAITING;
}
counter++;
}
Expand Down Expand Up @@ -290,10 +310,10 @@ byte getTradeCentreResponse(byte in, void* context) {
if(in == 0x6F) {
trade_centre_state = READY_TO_GO;
send = 0x6F;
pokemon_fap->gameboy_status = GAMEBOY_TRADE_READY;
gameboy_status = GAMEBOY_TRADE_READY;
} else if((in & 0x60) == 0x60) {
send = 0x60; // first pokemon
pokemon_fap->gameboy_status = GAMEBOY_SEND;
gameboy_status = GAMEBOY_SEND;
} else if(in == 0x00) {
send = 0;
trade_centre_state = TRADE_CONFIRMATION;
Expand All @@ -303,7 +323,7 @@ byte getTradeCentreResponse(byte in, void* context) {
case TRADE_CONFIRMATION:
if(in == 0x61) {
trade_centre_state = TRADE_PENDING;
pokemon_fap->gameboy_status = GAMEBOY_PENDING;
gameboy_status = GAMEBOY_PENDING;
} else if((in & 0x60) == 0x60) {
trade_centre_state = DONE;
}
Expand All @@ -313,7 +333,7 @@ byte getTradeCentreResponse(byte in, void* context) {
if(in == 0x00) {
send = 0;
trade_centre_state = INIT;
pokemon_fap->gameboy_status = GAMEBOY_TRADING;
gameboy_status = GAMEBOY_TRADING;
}
break;

Expand All @@ -322,24 +342,41 @@ byte getTradeCentreResponse(byte in, void* context) {
break;
}

with_view_model(
pokemon_fap->trade_view,
struct trade_model * model,
{ model->gameboy_status = gameboy_status; },
false);

return send;
}

void transferBit(void* context) {
PokemonFap* pokemon_fap = (PokemonFap*)context;
furi_assert(context);
bool connected;
bool trading;

with_view_model(
pokemon_fap->trade_view,
struct trade_model * model,
{
connected = model->connected;
trading = model->trading;
},
false);

byte raw_data = furi_hal_gpio_read(&GAME_BOY_SI);
in_data |= raw_data << (7 - shift);
if(++shift > 7) {
shift = 0;
switch(connection_state) {
case NOT_CONNECTED:
pokemon_fap->connected = false;
connected = false;
out_data = getConnectResponse(in_data);
break;
case CONNECTED:
pokemon_fap->connected = true;
connected = true;
out_data = getMenuResponse(in_data);
break;
case TRADE_CENTRE:
Expand All @@ -361,9 +398,18 @@ void transferBit(void* context) {
DELAY_MICROSECONDS); // Wait 20-60us ... 120us max (in slave mode is not necessary)
// TODO: The above comment doesn't make sense as DELAY_MICROSECONDS is defined as 15

if(trade_centre_state == READY_TO_GO) pokemon_fap->trading = true;
if(trade_centre_state == READY_TO_GO) trading = true;

out_data = out_data << 1;

with_view_model(
pokemon_fap->trade_view,
struct trade_model * model,
{
model->connected = connected;
model->trading = trading;
},
false);
}

void input_clk_gameboy(void* context) {
Expand All @@ -382,13 +428,32 @@ void input_clk_gameboy(void* context) {
time = micros();
}

void trade_draw_timer_callback(void* context) {
PokemonFap* pokemon_fap = (PokemonFap*)context;

with_view_model(
pokemon_fap->trade_view, struct trade_model * model, { UNUSED(model); }, true);
}

void trade_enter_callback(void* context) {
PokemonFap* pokemon_fap = (PokemonFap*)context;
furi_assert(context);

pokemon_fap->trading = false;
pokemon_fap->connected = false;
pokemon_fap->gameboy_status = GAMEBOY_INITIAL;
with_view_model(
pokemon_fap->trade_view,
struct trade_model * model,
{
model->trading = false;
model->connected = false;
model->gameboy_status = GAMEBOY_INITIAL;
model->pokemon_table = pokemon_fap->pokemon_table;
model->curr_pokemon = (uint8_t)pokemon_fap->curr_pokemon;
model->draw_timer =
furi_timer_alloc(trade_draw_timer_callback, FuriTimerTypePeriodic, pokemon_fap);
/* Every 100 ms, trigger a draw update */
furi_timer_start(model->draw_timer, 100);
},
true);

// B3 (Pin6) / SO (2)
furi_hal_gpio_write(&GAME_BOY_SO, false);
Expand All @@ -397,18 +462,9 @@ void trade_enter_callback(void* context) {
furi_hal_gpio_write(&GAME_BOY_SI, false);
furi_hal_gpio_init(&GAME_BOY_SI, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh);
// // C3 (Pin7) / CLK (5)
furi_hal_gpio_init(
&GAME_BOY_CLK,
GpioModeInterruptRise,
GpioPullNo,
GpioSpeedVeryHigh); // <-- This line causes the "OK" to stop functioning when exiting the application, so a reboot of the Flipper Zero is required.
furi_hal_gpio_init(&GAME_BOY_CLK, GpioModeInterruptRise, GpioPullNo, GpioSpeedVeryHigh);
furi_hal_gpio_remove_int_callback(&GAME_BOY_CLK);
furi_hal_gpio_add_int_callback(&GAME_BOY_CLK, input_clk_gameboy, pokemon_fap);

// furi_hal_gpio_disable_int_callback(&GAME_BOY_CLK);
// furi_hal_gpio_remove_int_callback(&GAME_BOY_CLK);
// Reset GPIO pins to default state
// furi_hal_gpio_init(&GAME_BOY_CLK, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
}

void disconnect_pin(const GpioPin* pin) {
Expand All @@ -417,11 +473,18 @@ void disconnect_pin(const GpioPin* pin) {
}

void trade_exit_callback(void* context) {
PokemonFap* pokemon_fap = (PokemonFap*)context;
furi_assert(context);
procesing = false;
furi_hal_light_set(LightGreen, 0x00);
furi_hal_light_set(LightBlue, 0x00);
furi_hal_light_set(LightRed, 0x00);
/* Stop the timer, and deallocate it as the enter callback allocates it on entry */
with_view_model(
pokemon_fap->trade_view,
struct trade_model * model,
{ furi_timer_free(model->draw_timer); },
false);
}

View* trade_alloc(PokemonFap* pokemon_fap) {
Expand All @@ -431,9 +494,7 @@ View* trade_alloc(PokemonFap* pokemon_fap) {
procesing = true;

view_set_context(view, pokemon_fap);
view_allocate_model(view, ViewModelTypeLockFree, sizeof(PokemonFap**));
with_view_model(
view, PokemonFap** model_fap, { *model_fap = pokemon_fap; }, false);
view_allocate_model(view, ViewModelTypeLockFree, sizeof(struct trade_model));

view_set_draw_callback(view, trade_draw_callback);
view_set_enter_callback(view, trade_enter_callback);
Expand Down

0 comments on commit 5884a74

Please sign in to comment.