trade.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. #include "../pokemon_app.h"
  2. #include "trade.hpp"
  3. #include "../pokemon_data.h"
  4. uint8_t out_data = 0;
  5. uint8_t in_data = 0;
  6. uint8_t shift = 0;
  7. uint32_t time = 0;
  8. volatile int counter = 0;
  9. volatile bool procesing = true;
  10. volatile connection_state_t connection_state = NOT_CONNECTED;
  11. volatile trade_centre_state_t trade_centre_state = INIT;
  12. void screen_gameboy_connect(Canvas* const canvas) {
  13. canvas_draw_frame(canvas, 0, 0, 128, 64);
  14. canvas_draw_icon(canvas, 1, 21, &I_Connect_me_62x31);
  15. canvas_draw_icon(canvas, 0, 53, &I_Background_128x11);
  16. canvas_draw_icon(canvas, 80, 0, &I_game_boy);
  17. canvas_draw_icon(canvas, 8, 2, &I_Space_65x18);
  18. canvas_draw_str(canvas, 18, 13, "Connect GB");
  19. }
  20. void screen_gameboy_connected(Canvas* const canvas) {
  21. canvas_draw_frame(canvas, 0, 0, 128, 64);
  22. canvas_draw_icon(canvas, 1, 21, &I_Connected_62x31);
  23. canvas_draw_icon(canvas, 0, 53, &I_Background_128x11);
  24. canvas_draw_icon(canvas, 80, 0, &I_game_boy);
  25. canvas_draw_icon(canvas, 8, 2, &I_Space_65x18);
  26. canvas_draw_str(canvas, 18, 13, "Connected!");
  27. }
  28. int time_in_seconds = 0;
  29. static void trade_draw_callback(Canvas* canvas, void* context) {
  30. const char* gameboy_status_text = NULL;
  31. canvas_clear(canvas);
  32. SelectPokemonModel* model = (SelectPokemonModel*)context;
  33. if(!model->trading) {
  34. if(!model->connected) {
  35. furi_hal_light_set(LightGreen, 0x00);
  36. furi_hal_light_set(LightBlue, 0x00);
  37. furi_hal_light_set(LightRed, 0xff);
  38. screen_gameboy_connect(canvas);
  39. } else {
  40. furi_hal_light_set(LightGreen, 0xff);
  41. furi_hal_light_set(LightBlue, 0x00);
  42. furi_hal_light_set(LightRed, 0x00);
  43. screen_gameboy_connected(canvas);
  44. }
  45. } else {
  46. switch(model->gameboy_status) {
  47. case GAMEBOY_TRADING:
  48. furi_hal_light_set(LightGreen, 0x00);
  49. furi_hal_light_set(LightRed, 0x00);
  50. if(time_in_seconds % 2 == 1) {
  51. furi_hal_light_set(LightBlue, 0xff);
  52. canvas_draw_icon(canvas, 0, 0, &I_gb_step_1);
  53. } else {
  54. furi_hal_light_set(LightBlue, 0x00);
  55. canvas_draw_icon(canvas, 0, 0, &I_gb_step_2);
  56. }
  57. break;
  58. case GAMEBOY_READY:
  59. case GAMEBOY_WAITING:
  60. case GAMEBOY_SEND:
  61. canvas_draw_icon(canvas, 38, 11, pokemon_table[model->current_pokemon].icon);
  62. break;
  63. default:
  64. // Default state added to eliminated enum warning
  65. break;
  66. }
  67. canvas_draw_icon(canvas, 0, 53, &I_Background_128x11);
  68. canvas_draw_frame(canvas, 0, 0, 128, 64);
  69. canvas_draw_icon(canvas, 24, 0, &I_Space_80x18);
  70. switch(model->gameboy_status) {
  71. case GAMEBOY_READY:
  72. gameboy_status_text = "READY";
  73. break;
  74. case GAMEBOY_WAITING:
  75. gameboy_status_text = "WAITING";
  76. break;
  77. case GAMEBOY_TRADE_READY:
  78. gameboy_status_text = "READY";
  79. break;
  80. case GAMEBOY_SEND:
  81. gameboy_status_text = "DEAL...";
  82. break;
  83. case GAMEBOY_PENDING:
  84. gameboy_status_text = "PENDING...";
  85. break;
  86. case GAMEBOY_TRADING:
  87. gameboy_status_text = "TRADING...";
  88. break;
  89. default:
  90. gameboy_status_text = "INITIAL";
  91. break;
  92. }
  93. canvas_draw_str(canvas, 48, 12, gameboy_status_text);
  94. canvas_draw_icon(canvas, 27, 1, &I_red_16x15);
  95. time_in_seconds = (int)DWT->CYCCNT / (72000000.0f / 4); // 250ms
  96. }
  97. }
  98. static bool trade_input_callback(InputEvent* event, void* context) {
  99. furi_assert(context);
  100. Trade* trade = (Trade*)context;
  101. bool consumed = false;
  102. if(event->type == InputTypePress && event->key == InputKeyBack) {
  103. view_dispatcher_switch_to_view(trade->app->view_dispatcher, AppViewSelectPokemon);
  104. consumed = true;
  105. }
  106. return consumed;
  107. }
  108. uint32_t micros() {
  109. return DWT->CYCCNT / 64;
  110. }
  111. /* Get the response byte from the link partner, updating the connection
  112. * state if needed.
  113. */
  114. byte getConnectResponse(byte in) {
  115. byte ret;
  116. switch(in) {
  117. case PKMN_CONNECTED:
  118. connection_state = CONNECTED;
  119. ret = PKMN_CONNECTED;
  120. break;
  121. case PKMN_MASTER:
  122. ret = PKMN_SLAVE;
  123. break;
  124. case PKMN_BLANK:
  125. ret = PKMN_BLANK;
  126. break;
  127. default:
  128. connection_state = NOT_CONNECTED;
  129. ret = PKMN_BREAK_LINK;
  130. break;
  131. }
  132. return ret;
  133. }
  134. /* Figure out what the pokemon game is requesting and move to that mode.
  135. */
  136. byte getMenuResponse(byte in) {
  137. /* TODO: Find out what this byte means */
  138. byte response = 0x00;
  139. switch(in) {
  140. case PKMN_CONNECTED:
  141. response = PKMN_CONNECTED;
  142. break;
  143. case PKMN_TRADE_CENTRE:
  144. connection_state = TRADE_CENTRE;
  145. break;
  146. case PKMN_COLOSSEUM:
  147. connection_state = COLOSSEUM;
  148. break;
  149. case PKMN_BREAK_LINK:
  150. case PKMN_MASTER:
  151. connection_state = NOT_CONNECTED;
  152. response = PKMN_BREAK_LINK;
  153. break;
  154. default:
  155. response = in;
  156. break;
  157. }
  158. return response;
  159. }
  160. byte getTradeCentreResponse(byte in, void* context) {
  161. UNUSED(context);
  162. Trade* trade = (Trade*)context;
  163. byte send = in;
  164. switch(trade_centre_state) {
  165. case INIT:
  166. // TODO: What does this value of in mean?
  167. if(in == 0x00) {
  168. // TODO: What does counter signify here?
  169. if(counter == 5) {
  170. trade_centre_state = READY_TO_GO;
  171. // CLICK EN LA MESA
  172. with_view_model_cpp(
  173. trade->view,
  174. SelectPokemonModel*,
  175. model,
  176. { model->gameboy_status = GAMEBOY_READY; },
  177. false);
  178. }
  179. counter++;
  180. }
  181. break;
  182. case READY_TO_GO:
  183. if((in & 0xF0) == 0xF0) trade_centre_state = SEEN_FIRST_WAIT;
  184. break;
  185. case SEEN_FIRST_WAIT:
  186. if((in & 0xF0) != 0xF0) {
  187. counter = 0;
  188. trade_centre_state = SENDING_RANDOM_DATA;
  189. }
  190. break;
  191. case SENDING_RANDOM_DATA:
  192. if((in & 0xF0) == 0xF0) {
  193. if(counter == 5) {
  194. trade_centre_state = WAITING_TO_SEND_DATA;
  195. with_view_model_cpp(
  196. trade->view,
  197. SelectPokemonModel*,
  198. model,
  199. { model->gameboy_status = GAMEBOY_WAITING; },
  200. false);
  201. }
  202. counter++;
  203. }
  204. break;
  205. case WAITING_TO_SEND_DATA:
  206. if((in & 0xF0) != 0xF0) {
  207. counter = 0;
  208. INPUT_BLOCK[counter] = in;
  209. send = DATA_BLOCK[counter];
  210. counter++;
  211. trade_centre_state = SENDING_DATA;
  212. }
  213. break;
  214. case SENDING_DATA:
  215. INPUT_BLOCK[counter] = in;
  216. send = DATA_BLOCK[counter];
  217. counter++;
  218. if(counter == 405) //TODO: replace with sizeof struct rather than static number
  219. trade_centre_state = SENDING_PATCH_DATA;
  220. break;
  221. case SENDING_PATCH_DATA:
  222. if(in == 0xFD) {
  223. counter = 0;
  224. send = 0xFD;
  225. } else {
  226. counter++;
  227. if(counter == 197) // TODO: What is this magic value?
  228. trade_centre_state = TRADE_PENDING;
  229. }
  230. break;
  231. case TRADE_PENDING:
  232. /* TODO: What are these states */
  233. if(in == 0x6F) {
  234. trade_centre_state = READY_TO_GO;
  235. send = 0x6F;
  236. with_view_model_cpp(
  237. trade->view,
  238. SelectPokemonModel*,
  239. model,
  240. { model->gameboy_status = GAMEBOY_TRADE_READY; },
  241. false);
  242. } else if((in & 0x60) == 0x60) {
  243. send = 0x60; // first pokemon
  244. with_view_model_cpp(
  245. trade->view,
  246. SelectPokemonModel*,
  247. model,
  248. { model->gameboy_status = GAMEBOY_SEND; },
  249. false);
  250. } else if(in == 0x00) {
  251. send = 0;
  252. trade_centre_state = TRADE_CONFIRMATION;
  253. }
  254. break;
  255. case TRADE_CONFIRMATION:
  256. if(in == 0x61) {
  257. trade_centre_state = TRADE_PENDING;
  258. with_view_model_cpp(
  259. trade->view,
  260. SelectPokemonModel*,
  261. model,
  262. { model->gameboy_status = GAMEBOY_PENDING; },
  263. false);
  264. } else if((in & 0x60) == 0x60) {
  265. trade_centre_state = DONE;
  266. }
  267. break;
  268. case DONE:
  269. if(in == 0x00) {
  270. send = 0;
  271. trade_centre_state = INIT;
  272. with_view_model_cpp(
  273. trade->view,
  274. SelectPokemonModel*,
  275. model,
  276. { model->gameboy_status = GAMEBOY_TRADING; },
  277. false);
  278. }
  279. break;
  280. default:
  281. // Do Nothing
  282. break;
  283. }
  284. return send;
  285. }
  286. void transferBit(void* context) {
  287. Trade* trade = (Trade*)context;
  288. byte raw_data = furi_hal_gpio_read(&GAME_BOY_SI);
  289. in_data |= raw_data << (7 - shift);
  290. if(++shift > 7) {
  291. shift = 0;
  292. switch(connection_state) {
  293. case NOT_CONNECTED:
  294. with_view_model_cpp(
  295. trade->view, SelectPokemonModel*, model, { model->connected = false; }, true);
  296. out_data = getConnectResponse(in_data);
  297. break;
  298. case CONNECTED:
  299. with_view_model_cpp(
  300. trade->view, SelectPokemonModel*, model, { model->connected = true; }, true);
  301. out_data = getMenuResponse(in_data);
  302. break;
  303. case TRADE_CENTRE:
  304. out_data = getTradeCentreResponse(in_data, trade);
  305. break;
  306. default:
  307. out_data = in_data;
  308. break;
  309. }
  310. in_data = 0; // TODO: I don't think this is necessary?
  311. }
  312. while(procesing && !furi_hal_gpio_read(&GAME_BOY_CLK))
  313. ;
  314. furi_hal_gpio_write(&GAME_BOY_SO, out_data & 0x80 ? true : false);
  315. furi_delay_us(
  316. DELAY_MICROSECONDS); // Wait 20-60us ... 120us max (in slave mode is not necessary)
  317. // TODO: The above comment doesn't make sense as DELAY_MICROSECONDS is defined as 15
  318. if(trade_centre_state == READY_TO_GO) {
  319. with_view_model_cpp(
  320. trade->view, SelectPokemonModel*, model, { model->trading = true; }, true);
  321. }
  322. out_data = out_data << 1;
  323. }
  324. void input_clk_gameboy(void* context) {
  325. furi_assert(context);
  326. if(time > 0) {
  327. // if there is no response from the master in 120 microseconds, the counters are reset
  328. if(micros() - time > 120) {
  329. // IDLE & Reset
  330. in_data = 0;
  331. shift = 0;
  332. }
  333. }
  334. transferBit(context);
  335. time = micros();
  336. }
  337. void trade_enter_callback(void* context) {
  338. furi_assert(context);
  339. Trade* trade = (Trade*)context;
  340. with_view_model_cpp(
  341. trade->view,
  342. SelectPokemonModel*,
  343. model,
  344. {
  345. model->current_pokemon = trade->app->current_pokemon;
  346. model->pokemon_hex_code = trade->app->pokemon_hex_code;
  347. model->trading = false;
  348. model->connected = false;
  349. model->gameboy_status = GAMEBOY_INITIAL;
  350. },
  351. true);
  352. DATA_BLOCK[12] = trade->app->pokemon_hex_code;
  353. // B3 (Pin6) / SO (2)
  354. furi_hal_gpio_write(&GAME_BOY_SO, false);
  355. furi_hal_gpio_init(&GAME_BOY_SO, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
  356. // B2 (Pin5) / SI (3)
  357. furi_hal_gpio_write(&GAME_BOY_SI, false);
  358. furi_hal_gpio_init(&GAME_BOY_SI, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh);
  359. // // C3 (Pin7) / CLK (5)
  360. furi_hal_gpio_init(
  361. &GAME_BOY_CLK,
  362. GpioModeInterruptRise,
  363. GpioPullNo,
  364. GpioSpeedVeryHigh); // <-- This line causes the "OK" to stop functioning when exiting the application, so a reboot of the Flipper Zero is required.
  365. furi_hal_gpio_remove_int_callback(&GAME_BOY_CLK);
  366. furi_hal_gpio_add_int_callback(&GAME_BOY_CLK, input_clk_gameboy, trade);
  367. // furi_hal_gpio_disable_int_callback(&GAME_BOY_CLK);
  368. // furi_hal_gpio_remove_int_callback(&GAME_BOY_CLK);
  369. // Reset GPIO pins to default state
  370. // furi_hal_gpio_init(&GAME_BOY_CLK, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
  371. }
  372. bool trade_custom_callback(uint32_t event, void* context) {
  373. UNUSED(event);
  374. furi_assert(context);
  375. Trade* trade = (Trade*)context;
  376. view_dispatcher_send_custom_event(trade->app->view_dispatcher, 0);
  377. return true;
  378. }
  379. void disconnect_pin(const GpioPin* pin) {
  380. furi_hal_gpio_init(pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
  381. furi_hal_gpio_write(pin, true);
  382. }
  383. void trade_exit_callback(void* context) {
  384. furi_assert(context);
  385. procesing = false;
  386. furi_hal_light_set(LightGreen, 0x00);
  387. furi_hal_light_set(LightBlue, 0x00);
  388. furi_hal_light_set(LightRed, 0x00);
  389. }
  390. Trade* trade_alloc(App* app) {
  391. Trade* trade = (Trade*)malloc(sizeof(Trade));
  392. trade->app = app;
  393. trade->view = view_alloc();
  394. procesing = true;
  395. view_set_context(trade->view, trade);
  396. view_allocate_model(trade->view, ViewModelTypeLockFree, sizeof(SelectPokemonModel));
  397. view_set_draw_callback(trade->view, trade_draw_callback);
  398. view_set_input_callback(trade->view, trade_input_callback);
  399. view_set_enter_callback(trade->view, trade_enter_callback);
  400. view_set_custom_callback(trade->view, trade_custom_callback);
  401. view_set_exit_callback(trade->view, trade_exit_callback);
  402. return trade;
  403. }
  404. void trade_free(App* app) {
  405. furi_assert(app);
  406. // Free resources
  407. procesing = false;
  408. furi_hal_gpio_remove_int_callback(&GAME_BOY_CLK);
  409. disconnect_pin(&GAME_BOY_CLK);
  410. view_free(app->trade->view);
  411. free(app->trade);
  412. }
  413. View* trade_get_view(App* app) {
  414. furi_assert(app);
  415. return app->trade->view;
  416. }