flipper_http.h 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330
  1. // flipper_http.h
  2. #ifndef FLIPPER_HTTP_H
  3. #define FLIPPER_HTTP_H
  4. #include <furi.h>
  5. #include <furi_hal.h>
  6. #include <furi_hal_gpio.h>
  7. #include <furi_hal_serial.h>
  8. #include <storage/storage.h>
  9. // STORAGE_EXT_PATH_PREFIX is defined in the Furi SDK as /ext
  10. #define HTTP_TAG "FlipWiFi" // change this to your app name
  11. #define http_tag "flip_wifi" // change this to your app id
  12. #define UART_CH (FuriHalSerialIdUsart) // UART channel
  13. #define TIMEOUT_DURATION_TICKS (2 * 1000) // 2 seconds
  14. #define BAUDRATE (115200) // UART baudrate
  15. #define RX_BUF_SIZE 128 // UART RX buffer size
  16. #define RX_LINE_BUFFER_SIZE 256 // UART RX line buffer size (increase for large responses)
  17. // Forward declaration for callback
  18. typedef void (*FlipperHTTP_Callback)(const char *line, void *context);
  19. // Functions
  20. bool flipper_http_init(FlipperHTTP_Callback callback, void *context);
  21. void flipper_http_deinit();
  22. //---
  23. void flipper_http_rx_callback(const char *line, void *context);
  24. bool flipper_http_send_data(const char *data);
  25. //---
  26. bool flipper_http_connect_wifi();
  27. bool flipper_http_disconnect_wifi();
  28. bool flipper_http_ping();
  29. bool flipper_http_scan_wifi();
  30. bool flipper_http_save_wifi(const char *ssid, const char *password);
  31. bool flipper_http_ip_wifi();
  32. bool flipper_http_ip_address();
  33. //---
  34. bool flipper_http_list_commands();
  35. bool flipper_http_led_on();
  36. bool flipper_http_led_off();
  37. bool flipper_http_parse_json(const char *key, const char *json_data);
  38. bool flipper_http_parse_json_array(const char *key, int index, const char *json_data);
  39. //---
  40. bool flipper_http_get_request(const char *url);
  41. bool flipper_http_get_request_with_headers(const char *url, const char *headers);
  42. bool flipper_http_post_request_with_headers(const char *url, const char *headers, const char *payload);
  43. bool flipper_http_put_request_with_headers(const char *url, const char *headers, const char *payload);
  44. bool flipper_http_delete_request_with_headers(const char *url, const char *headers, const char *payload);
  45. //---
  46. bool flipper_http_save_received_data(size_t bytes_received, const char line_buffer[]);
  47. static char *trim(const char *str);
  48. // State variable to track the UART state
  49. typedef enum
  50. {
  51. INACTIVE, // Inactive state
  52. IDLE, // Default state
  53. RECEIVING, // Receiving data
  54. SENDING, // Sending data
  55. ISSUE, // Issue with connection
  56. } SerialState;
  57. // Event Flags for UART Worker Thread
  58. typedef enum
  59. {
  60. WorkerEvtStop = (1 << 0),
  61. WorkerEvtRxDone = (1 << 1),
  62. } WorkerEvtFlags;
  63. // FlipperHTTP Structure
  64. typedef struct
  65. {
  66. FuriStreamBuffer *flipper_http_stream; // Stream buffer for UART communication
  67. FuriHalSerialHandle *serial_handle; // Serial handle for UART communication
  68. FuriThread *rx_thread; // Worker thread for UART
  69. uint8_t rx_buf[RX_BUF_SIZE]; // Buffer for received data
  70. FuriThreadId rx_thread_id; // Worker thread ID
  71. FlipperHTTP_Callback handle_rx_line_cb; // Callback for received lines
  72. void *callback_context; // Context for the callback
  73. SerialState state; // State of the UART
  74. // variable to store the last received data from the UART
  75. char *last_response;
  76. // Timer-related members
  77. FuriTimer *get_timeout_timer; // Timer for HTTP request timeout
  78. char *received_data; // Buffer to store received data
  79. bool started_receiving_get; // Indicates if a GET request has started
  80. bool just_started_get; // Indicates if GET data reception has just started
  81. bool started_receiving_post; // Indicates if a POST request has started
  82. bool just_started_post; // Indicates if POST data reception has just started
  83. bool started_receiving_put; // Indicates if a PUT request has started
  84. bool just_started_put; // Indicates if PUT data reception has just started
  85. bool started_receiving_delete; // Indicates if a DELETE request has started
  86. bool just_started_delete; // Indicates if DELETE data reception has just started
  87. } FlipperHTTP;
  88. FlipperHTTP fhttp;
  89. // fhttp.received_data holds the received data from HTTP requests
  90. // fhttp.last_response holds the last received data from the UART, which could be [GET/END], [POST/END], [PUT/END], [DELETE/END], etc
  91. // Timer callback function
  92. /**
  93. * @brief Callback function for the GET timeout timer.
  94. * @return 0
  95. * @param context The context to pass to the callback.
  96. * @note This function will be called when the GET request times out.
  97. */
  98. void get_timeout_timer_callback(void *context)
  99. {
  100. UNUSED(context);
  101. FURI_LOG_E(HTTP_TAG, "Timeout reached: 2 seconds without receiving the end.");
  102. // Reset the state
  103. fhttp.started_receiving_get = false;
  104. fhttp.started_receiving_post = false;
  105. fhttp.started_receiving_put = false;
  106. fhttp.started_receiving_delete = false;
  107. // Free received data if any
  108. if (fhttp.received_data)
  109. {
  110. free(fhttp.received_data);
  111. fhttp.received_data = NULL;
  112. }
  113. // Update UART state
  114. fhttp.state = ISSUE;
  115. }
  116. // UART RX Handler Callback (Interrupt Context)
  117. /**
  118. * @brief A private callback function to handle received data asynchronously.
  119. * @return void
  120. * @param handle The UART handle.
  121. * @param event The event type.
  122. * @param context The context to pass to the callback.
  123. * @note This function will handle received data asynchronously via the callback.
  124. */
  125. static void _flipper_http_rx_callback(FuriHalSerialHandle *handle, FuriHalSerialRxEvent event, void *context)
  126. {
  127. UNUSED(context);
  128. if (event == FuriHalSerialRxEventData)
  129. {
  130. uint8_t data = furi_hal_serial_async_rx(handle);
  131. furi_stream_buffer_send(fhttp.flipper_http_stream, &data, 1, 0);
  132. furi_thread_flags_set(fhttp.rx_thread_id, WorkerEvtRxDone);
  133. }
  134. }
  135. // UART worker thread
  136. /**
  137. * @brief Worker thread to handle UART data asynchronously.
  138. * @return 0
  139. * @param context The context to pass to the callback.
  140. * @note This function will handle received data asynchronously via the callback.
  141. */
  142. static int32_t flipper_http_worker(void *context)
  143. {
  144. UNUSED(context);
  145. size_t rx_line_pos = 0;
  146. char *rx_line_buffer = (char *)malloc(RX_LINE_BUFFER_SIZE);
  147. if (!rx_line_buffer)
  148. {
  149. // Handle malloc failure
  150. FURI_LOG_E(HTTP_TAG, "Failed to allocate memory for rx_line_buffer");
  151. return -1;
  152. }
  153. while (1)
  154. {
  155. uint32_t events = furi_thread_flags_wait(WorkerEvtStop | WorkerEvtRxDone, FuriFlagWaitAny, FuriWaitForever);
  156. if (events & WorkerEvtStop)
  157. break;
  158. if (events & WorkerEvtRxDone)
  159. {
  160. size_t len = furi_stream_buffer_receive(fhttp.flipper_http_stream, fhttp.rx_buf, RX_BUF_SIZE, 0);
  161. for (size_t i = 0; i < len; i++)
  162. {
  163. char c = fhttp.rx_buf[i];
  164. if (c == '\n' || rx_line_pos >= RX_LINE_BUFFER_SIZE - 1)
  165. {
  166. rx_line_buffer[rx_line_pos] = '\0';
  167. // Invoke the callback with the complete line
  168. if (fhttp.handle_rx_line_cb)
  169. {
  170. fhttp.handle_rx_line_cb(rx_line_buffer, fhttp.callback_context);
  171. }
  172. // Reset the line buffer
  173. rx_line_pos = 0;
  174. }
  175. else
  176. {
  177. rx_line_buffer[rx_line_pos++] = c;
  178. }
  179. }
  180. }
  181. }
  182. // Free the allocated memory before exiting the thread
  183. free(rx_line_buffer);
  184. return 0;
  185. }
  186. // UART initialization function
  187. /**
  188. * @brief Initialize UART.
  189. * @return true if the UART was initialized successfully, false otherwise.
  190. * @param callback The callback function to handle received data (ex. flipper_http_rx_callback).
  191. * @param context The context to pass to the callback.
  192. * @note The received data will be handled asynchronously via the callback.
  193. */
  194. bool flipper_http_init(FlipperHTTP_Callback callback, void *context)
  195. {
  196. if (!context)
  197. {
  198. FURI_LOG_E(HTTP_TAG, "Invalid context provided to flipper_http_init.");
  199. return false;
  200. }
  201. if (!callback)
  202. {
  203. FURI_LOG_E(HTTP_TAG, "Invalid callback provided to flipper_http_init.");
  204. return false;
  205. }
  206. fhttp.flipper_http_stream = furi_stream_buffer_alloc(RX_BUF_SIZE, 1);
  207. if (!fhttp.flipper_http_stream)
  208. {
  209. FURI_LOG_E(HTTP_TAG, "Failed to allocate UART stream buffer.");
  210. return false;
  211. }
  212. fhttp.rx_thread = furi_thread_alloc();
  213. if (!fhttp.rx_thread)
  214. {
  215. FURI_LOG_E(HTTP_TAG, "Failed to allocate UART thread.");
  216. furi_stream_buffer_free(fhttp.flipper_http_stream);
  217. return false;
  218. }
  219. furi_thread_set_name(fhttp.rx_thread, "FlipperHTTP_RxThread");
  220. furi_thread_set_stack_size(fhttp.rx_thread, 1024);
  221. furi_thread_set_context(fhttp.rx_thread, &fhttp);
  222. furi_thread_set_callback(fhttp.rx_thread, flipper_http_worker);
  223. fhttp.handle_rx_line_cb = callback;
  224. fhttp.callback_context = context;
  225. furi_thread_start(fhttp.rx_thread);
  226. fhttp.rx_thread_id = furi_thread_get_id(fhttp.rx_thread);
  227. // handle when the UART control is busy to avoid furi_check failed
  228. if (furi_hal_serial_control_is_busy(UART_CH))
  229. {
  230. FURI_LOG_E(HTTP_TAG, "UART control is busy.");
  231. return false;
  232. }
  233. fhttp.serial_handle = furi_hal_serial_control_acquire(UART_CH);
  234. if (!fhttp.serial_handle)
  235. {
  236. FURI_LOG_E(HTTP_TAG, "Failed to acquire UART control - handle is NULL");
  237. // Cleanup resources
  238. furi_thread_free(fhttp.rx_thread);
  239. furi_stream_buffer_free(fhttp.flipper_http_stream);
  240. return false;
  241. }
  242. // Initialize UART with acquired handle
  243. furi_hal_serial_init(fhttp.serial_handle, BAUDRATE);
  244. // Enable RX direction
  245. furi_hal_serial_enable_direction(fhttp.serial_handle, FuriHalSerialDirectionRx);
  246. // Start asynchronous RX with the callback
  247. furi_hal_serial_async_rx_start(fhttp.serial_handle, _flipper_http_rx_callback, &fhttp, false);
  248. // Wait for the TX to complete to ensure UART is ready
  249. furi_hal_serial_tx_wait_complete(fhttp.serial_handle);
  250. // Allocate the timer for handling timeouts
  251. fhttp.get_timeout_timer = furi_timer_alloc(
  252. get_timeout_timer_callback, // Callback function
  253. FuriTimerTypeOnce, // One-shot timer
  254. &fhttp // Context passed to callback
  255. );
  256. if (!fhttp.get_timeout_timer)
  257. {
  258. FURI_LOG_E(HTTP_TAG, "Failed to allocate HTTP request timeout timer.");
  259. // Cleanup resources
  260. furi_hal_serial_async_rx_stop(fhttp.serial_handle);
  261. furi_hal_serial_disable_direction(fhttp.serial_handle, FuriHalSerialDirectionRx);
  262. furi_hal_serial_control_release(fhttp.serial_handle);
  263. furi_hal_serial_deinit(fhttp.serial_handle);
  264. furi_thread_flags_set(fhttp.rx_thread_id, WorkerEvtStop);
  265. furi_thread_join(fhttp.rx_thread);
  266. furi_thread_free(fhttp.rx_thread);
  267. furi_stream_buffer_free(fhttp.flipper_http_stream);
  268. return false;
  269. }
  270. // Set the timer thread priority if needed
  271. furi_timer_set_thread_priority(FuriTimerThreadPriorityElevated);
  272. // FURI_LOG_I(HTTP_TAG, "UART initialized successfully.");
  273. return true;
  274. }
  275. // Deinitialize UART
  276. /**
  277. * @brief Deinitialize UART.
  278. * @return void
  279. * @note This function will stop the asynchronous RX, release the serial handle, and free the resources.
  280. */
  281. void flipper_http_deinit()
  282. {
  283. if (fhttp.serial_handle == NULL)
  284. {
  285. FURI_LOG_E(HTTP_TAG, "UART handle is NULL. Already deinitialized?");
  286. return;
  287. }
  288. // Stop asynchronous RX
  289. furi_hal_serial_async_rx_stop(fhttp.serial_handle);
  290. // Release and deinitialize the serial handle
  291. furi_hal_serial_disable_direction(fhttp.serial_handle, FuriHalSerialDirectionRx);
  292. furi_hal_serial_control_release(fhttp.serial_handle);
  293. furi_hal_serial_deinit(fhttp.serial_handle);
  294. // Signal the worker thread to stop
  295. furi_thread_flags_set(fhttp.rx_thread_id, WorkerEvtStop);
  296. // Wait for the thread to finish
  297. furi_thread_join(fhttp.rx_thread);
  298. // Free the thread resources
  299. furi_thread_free(fhttp.rx_thread);
  300. // Free the stream buffer
  301. furi_stream_buffer_free(fhttp.flipper_http_stream);
  302. // Free the timer
  303. if (fhttp.get_timeout_timer)
  304. {
  305. furi_timer_free(fhttp.get_timeout_timer);
  306. fhttp.get_timeout_timer = NULL;
  307. }
  308. // Free received data if any
  309. if (fhttp.received_data)
  310. {
  311. free(fhttp.received_data);
  312. fhttp.received_data = NULL;
  313. }
  314. // Free the last response
  315. if (fhttp.last_response)
  316. {
  317. free(fhttp.last_response);
  318. fhttp.last_response = NULL;
  319. }
  320. // FURI_LOG_I("FlipperHTTP", "UART deinitialized successfully.");
  321. }
  322. // Function to send data over UART with newline termination
  323. /**
  324. * @brief Send data over UART with newline termination.
  325. * @return true if the data was sent successfully, false otherwise.
  326. * @param data The data to send over UART.
  327. * @note The data will be sent over UART with a newline character appended.
  328. */
  329. bool flipper_http_send_data(const char *data)
  330. {
  331. size_t data_length = strlen(data);
  332. if (data_length == 0)
  333. {
  334. FURI_LOG_E("FlipperHTTP", "Attempted to send empty data.");
  335. return false;
  336. }
  337. // Create a buffer with data + '\n'
  338. size_t send_length = data_length + 1; // +1 for '\n'
  339. if (send_length > 256)
  340. { // Ensure buffer size is sufficient
  341. FURI_LOG_E("FlipperHTTP", "Data too long to send over FHTTP.");
  342. return false;
  343. }
  344. char send_buffer[257]; // 256 + 1 for safety
  345. strncpy(send_buffer, data, 256);
  346. send_buffer[data_length] = '\n'; // Append newline
  347. send_buffer[data_length + 1] = '\0'; // Null-terminate
  348. if (fhttp.state == INACTIVE && ((strstr(send_buffer, "[PING]") == NULL) && (strstr(send_buffer, "[WIFI/CONNECT]") == NULL)))
  349. {
  350. FURI_LOG_E("FlipperHTTP", "Cannot send data while INACTIVE.");
  351. fhttp.last_response = "Cannot send data while INACTIVE.";
  352. return false;
  353. }
  354. fhttp.state = SENDING;
  355. furi_hal_serial_tx(fhttp.serial_handle, (const uint8_t *)send_buffer, send_length);
  356. // Uncomment below line to log the data sent over UART
  357. // FURI_LOG_I("FlipperHTTP", "Sent data over UART: %s", send_buffer);
  358. fhttp.state = IDLE;
  359. return true;
  360. }
  361. // Function to send a PING request
  362. /**
  363. * @brief Send a PING request to check if the Wifi Dev Board is connected.
  364. * @return true if the request was successful, false otherwise.
  365. * @note The received data will be handled asynchronously via the callback.
  366. * @note This is best used to check if the Wifi Dev Board is connected.
  367. * @note The state will remain INACTIVE until a PONG is received.
  368. */
  369. bool flipper_http_ping()
  370. {
  371. const char *command = "[PING]";
  372. if (!flipper_http_send_data(command))
  373. {
  374. FURI_LOG_E("FlipperHTTP", "Failed to send PING command.");
  375. return false;
  376. }
  377. // set state as INACTIVE to be made IDLE if PONG is received
  378. fhttp.state = INACTIVE;
  379. // The response will be handled asynchronously via the callback
  380. return true;
  381. }
  382. // Function to list available commands
  383. /**
  384. * @brief Send a command to list available commands.
  385. * @return true if the request was successful, false otherwise.
  386. * @note The received data will be handled asynchronously via the callback.
  387. */
  388. bool flipper_http_list_commands()
  389. {
  390. const char *command = "[LIST]";
  391. if (!flipper_http_send_data(command))
  392. {
  393. FURI_LOG_E("FlipperHTTP", "Failed to send LIST command.");
  394. return false;
  395. }
  396. // The response will be handled asynchronously via the callback
  397. return true;
  398. }
  399. // Function to turn on the LED
  400. /**
  401. * @brief Allow the LED to display while processing.
  402. * @return true if the request was successful, false otherwise.
  403. * @note The received data will be handled asynchronously via the callback.
  404. */
  405. bool flipper_http_led_on()
  406. {
  407. const char *command = "[LED/ON]";
  408. if (!flipper_http_send_data(command))
  409. {
  410. FURI_LOG_E("FlipperHTTP", "Failed to send LED ON command.");
  411. return false;
  412. }
  413. // The response will be handled asynchronously via the callback
  414. return true;
  415. }
  416. // Function to turn off the LED
  417. /**
  418. * @brief Disable the LED from displaying while processing.
  419. * @return true if the request was successful, false otherwise.
  420. * @note The received data will be handled asynchronously via the callback.
  421. */
  422. bool flipper_http_led_off()
  423. {
  424. const char *command = "[LED/OFF]";
  425. if (!flipper_http_send_data(command))
  426. {
  427. FURI_LOG_E("FlipperHTTP", "Failed to send LED OFF command.");
  428. return false;
  429. }
  430. // The response will be handled asynchronously via the callback
  431. return true;
  432. }
  433. // Function to parse JSON data
  434. /**
  435. * @brief Parse JSON data.
  436. * @return true if the JSON data was parsed successfully, false otherwise.
  437. * @param key The key to parse from the JSON data.
  438. * @param json_data The JSON data to parse.
  439. * @note The received data will be handled asynchronously via the callback.
  440. */
  441. bool flipper_http_parse_json(const char *key, const char *json_data)
  442. {
  443. if (!key || !json_data)
  444. {
  445. FURI_LOG_E("FlipperHTTP", "Invalid arguments provided to flipper_http_parse_json.");
  446. return false;
  447. }
  448. char buffer[256];
  449. int ret = snprintf(buffer, sizeof(buffer), "[PARSE]{\"key\":\"%s\",\"json\":%s}", key, json_data);
  450. if (ret < 0 || ret >= (int)sizeof(buffer))
  451. {
  452. FURI_LOG_E("FlipperHTTP", "Failed to format JSON parse command.");
  453. return false;
  454. }
  455. if (!flipper_http_send_data(buffer))
  456. {
  457. FURI_LOG_E("FlipperHTTP", "Failed to send JSON parse command.");
  458. return false;
  459. }
  460. // The response will be handled asynchronously via the callback
  461. return true;
  462. }
  463. // Function to parse JSON array data
  464. /**
  465. * @brief Parse JSON array data.
  466. * @return true if the JSON array data was parsed successfully, false otherwise.
  467. * @param key The key to parse from the JSON array data.
  468. * @param index The index to parse from the JSON array data.
  469. * @param json_data The JSON array data to parse.
  470. * @note The received data will be handled asynchronously via the callback.
  471. */
  472. bool flipper_http_parse_json_array(const char *key, int index, const char *json_data)
  473. {
  474. if (!key || !json_data)
  475. {
  476. FURI_LOG_E("FlipperHTTP", "Invalid arguments provided to flipper_http_parse_json_array.");
  477. return false;
  478. }
  479. char buffer[256];
  480. int ret = snprintf(buffer, sizeof(buffer), "[PARSE/ARRAY]{\"key\":\"%s\",\"index\":%d,\"json\":%s}", key, index, json_data);
  481. if (ret < 0 || ret >= (int)sizeof(buffer))
  482. {
  483. FURI_LOG_E("FlipperHTTP", "Failed to format JSON parse array command.");
  484. return false;
  485. }
  486. if (!flipper_http_send_data(buffer))
  487. {
  488. FURI_LOG_E("FlipperHTTP", "Failed to send JSON parse array command.");
  489. return false;
  490. }
  491. // The response will be handled asynchronously via the callback
  492. return true;
  493. }
  494. // Function to scan for WiFi networks
  495. /**
  496. * @brief Send a command to scan for WiFi networks.
  497. * @return true if the request was successful, false otherwise.
  498. * @note The received data will be handled asynchronously via the callback.
  499. */
  500. bool flipper_http_scan_wifi()
  501. {
  502. const char *command = "[WIFI/SCAN]";
  503. if (!flipper_http_send_data(command))
  504. {
  505. FURI_LOG_E("FlipperHTTP", "Failed to send WiFi scan command.");
  506. return false;
  507. }
  508. // The response will be handled asynchronously via the callback
  509. return true;
  510. }
  511. // Function to save WiFi settings (returns true if successful)
  512. /**
  513. * @brief Send a command to save WiFi settings.
  514. * @return true if the request was successful, false otherwise.
  515. * @note The received data will be handled asynchronously via the callback.
  516. */
  517. bool flipper_http_save_wifi(const char *ssid, const char *password)
  518. {
  519. if (!ssid || !password)
  520. {
  521. FURI_LOG_E("FlipperHTTP", "Invalid arguments provided to flipper_http_save_wifi.");
  522. return false;
  523. }
  524. char buffer[256];
  525. int ret = snprintf(buffer, sizeof(buffer), "[WIFI/SAVE]{\"ssid\":\"%s\",\"password\":\"%s\"}", ssid, password);
  526. if (ret < 0 || ret >= (int)sizeof(buffer))
  527. {
  528. FURI_LOG_E("FlipperHTTP", "Failed to format WiFi save command.");
  529. return false;
  530. }
  531. if (!flipper_http_send_data(buffer))
  532. {
  533. FURI_LOG_E("FlipperHTTP", "Failed to send WiFi save command.");
  534. return false;
  535. }
  536. // The response will be handled asynchronously via the callback
  537. return true;
  538. }
  539. // Function to get IP address of WiFi Devboard
  540. /**
  541. * @brief Send a command to get the IP address of the WiFi Devboard
  542. * @return true if the request was successful, false otherwise.
  543. * @note The received data will be handled asynchronously via the callback.
  544. */
  545. bool flipper_http_ip_address()
  546. {
  547. const char *command = "[IP/ADDRESS]";
  548. if (!flipper_http_send_data(command))
  549. {
  550. FURI_LOG_E("FlipperHTTP", "Failed to send IP address command.");
  551. return false;
  552. }
  553. // The response will be handled asynchronously via the callback
  554. return true;
  555. }
  556. // Function to get IP address of the connected WiFi network
  557. /**
  558. * @brief Send a command to get the IP address of the connected WiFi network.
  559. * @return true if the request was successful, false otherwise.
  560. * @note The received data will be handled asynchronously via the callback.
  561. */
  562. bool flipper_http_ip_wifi()
  563. {
  564. const char *command = "[WIFI/IP]";
  565. if (!flipper_http_send_data(command))
  566. {
  567. FURI_LOG_E("FlipperHTTP", "Failed to send WiFi IP command.");
  568. return false;
  569. }
  570. // The response will be handled asynchronously via the callback
  571. return true;
  572. }
  573. // Function to disconnect from WiFi (returns true if successful)
  574. /**
  575. * @brief Send a command to disconnect from WiFi.
  576. * @return true if the request was successful, false otherwise.
  577. * @note The received data will be handled asynchronously via the callback.
  578. */
  579. bool flipper_http_disconnect_wifi()
  580. {
  581. const char *command = "[WIFI/DISCONNECT]";
  582. if (!flipper_http_send_data(command))
  583. {
  584. FURI_LOG_E("FlipperHTTP", "Failed to send WiFi disconnect command.");
  585. return false;
  586. }
  587. // The response will be handled asynchronously via the callback
  588. return true;
  589. }
  590. // Function to connect to WiFi (returns true if successful)
  591. /**
  592. * @brief Send a command to connect to WiFi.
  593. * @return true if the request was successful, false otherwise.
  594. * @note The received data will be handled asynchronously via the callback.
  595. */
  596. bool flipper_http_connect_wifi()
  597. {
  598. const char *command = "[WIFI/CONNECT]";
  599. if (!flipper_http_send_data(command))
  600. {
  601. FURI_LOG_E("FlipperHTTP", "Failed to send WiFi connect command.");
  602. return false;
  603. }
  604. // The response will be handled asynchronously via the callback
  605. return true;
  606. }
  607. // Function to send a GET request
  608. /**
  609. * @brief Send a GET request to the specified URL.
  610. * @return true if the request was successful, false otherwise.
  611. * @param url The URL to send the GET request to.
  612. * @note The received data will be handled asynchronously via the callback.
  613. */
  614. bool flipper_http_get_request(const char *url)
  615. {
  616. if (!url)
  617. {
  618. FURI_LOG_E("FlipperHTTP", "Invalid arguments provided to flipper_http_get_request.");
  619. return false;
  620. }
  621. // Prepare GET request command
  622. char command[256];
  623. int ret = snprintf(command, sizeof(command), "[GET]%s", url);
  624. if (ret < 0 || ret >= (int)sizeof(command))
  625. {
  626. FURI_LOG_E("FlipperHTTP", "Failed to format GET request command.");
  627. return false;
  628. }
  629. // Send GET request via UART
  630. if (!flipper_http_send_data(command))
  631. {
  632. FURI_LOG_E("FlipperHTTP", "Failed to send GET request command.");
  633. return false;
  634. }
  635. // The response will be handled asynchronously via the callback
  636. return true;
  637. }
  638. // Function to send a GET request with headers
  639. /**
  640. * @brief Send a GET request to the specified URL.
  641. * @return true if the request was successful, false otherwise.
  642. * @param url The URL to send the GET request to.
  643. * @param headers The headers to send with the GET request.
  644. * @note The received data will be handled asynchronously via the callback.
  645. */
  646. bool flipper_http_get_request_with_headers(const char *url, const char *headers)
  647. {
  648. if (!url || !headers)
  649. {
  650. FURI_LOG_E("FlipperHTTP", "Invalid arguments provided to flipper_http_get_request_with_headers.");
  651. return false;
  652. }
  653. // Prepare GET request command with headers
  654. char command[256];
  655. int ret = snprintf(command, sizeof(command), "[GET/HTTP]{\"url\":\"%s\",\"headers\":%s}", url, headers);
  656. if (ret < 0 || ret >= (int)sizeof(command))
  657. {
  658. FURI_LOG_E("FlipperHTTP", "Failed to format GET request command with headers.");
  659. return false;
  660. }
  661. // Send GET request via UART
  662. if (!flipper_http_send_data(command))
  663. {
  664. FURI_LOG_E("FlipperHTTP", "Failed to send GET request command with headers.");
  665. return false;
  666. }
  667. // The response will be handled asynchronously via the callback
  668. return true;
  669. }
  670. // Function to send a POST request with headers
  671. /**
  672. * @brief Send a POST request to the specified URL.
  673. * @return true if the request was successful, false otherwise.
  674. * @param url The URL to send the POST request to.
  675. * @param headers The headers to send with the POST request.
  676. * @param data The data to send with the POST request.
  677. * @note The received data will be handled asynchronously via the callback.
  678. */
  679. bool flipper_http_post_request_with_headers(const char *url, const char *headers, const char *payload)
  680. {
  681. if (!url || !headers || !payload)
  682. {
  683. FURI_LOG_E("FlipperHTTP", "Invalid arguments provided to flipper_http_post_request_with_headers.");
  684. return false;
  685. }
  686. // Prepare POST request command with headers and data
  687. char command[256];
  688. int ret = snprintf(command, sizeof(command), "[POST/HTTP]{\"url\":\"%s\",\"headers\":%s,\"payload\":%s}", url, headers, payload);
  689. if (ret < 0 || ret >= (int)sizeof(command))
  690. {
  691. FURI_LOG_E("FlipperHTTP", "Failed to format POST request command with headers and data.");
  692. return false;
  693. }
  694. // Send POST request via UART
  695. if (!flipper_http_send_data(command))
  696. {
  697. FURI_LOG_E("FlipperHTTP", "Failed to send POST request command with headers and data.");
  698. return false;
  699. }
  700. // The response will be handled asynchronously via the callback
  701. return true;
  702. }
  703. // Function to send a PUT request with headers
  704. /**
  705. * @brief Send a PUT request to the specified URL.
  706. * @return true if the request was successful, false otherwise.
  707. * @param url The URL to send the PUT request to.
  708. * @param headers The headers to send with the PUT request.
  709. * @param data The data to send with the PUT request.
  710. * @note The received data will be handled asynchronously via the callback.
  711. */
  712. bool flipper_http_put_request_with_headers(const char *url, const char *headers, const char *payload)
  713. {
  714. if (!url || !headers || !payload)
  715. {
  716. FURI_LOG_E("FlipperHTTP", "Invalid arguments provided to flipper_http_put_request_with_headers.");
  717. return false;
  718. }
  719. // Prepare PUT request command with headers and data
  720. char command[256];
  721. int ret = snprintf(command, sizeof(command), "[PUT/HTTP]{\"url\":\"%s\",\"headers\":%s,\"payload\":%s}", url, headers, payload);
  722. if (ret < 0 || ret >= (int)sizeof(command))
  723. {
  724. FURI_LOG_E("FlipperHTTP", "Failed to format PUT request command with headers and data.");
  725. return false;
  726. }
  727. // Send PUT request via UART
  728. if (!flipper_http_send_data(command))
  729. {
  730. FURI_LOG_E("FlipperHTTP", "Failed to send PUT request command with headers and data.");
  731. return false;
  732. }
  733. // The response will be handled asynchronously via the callback
  734. return true;
  735. }
  736. // Function to send a DELETE request with headers
  737. /**
  738. * @brief Send a DELETE request to the specified URL.
  739. * @return true if the request was successful, false otherwise.
  740. * @param url The URL to send the DELETE request to.
  741. * @param headers The headers to send with the DELETE request.
  742. * @param data The data to send with the DELETE request.
  743. * @note The received data will be handled asynchronously via the callback.
  744. */
  745. bool flipper_http_delete_request_with_headers(const char *url, const char *headers, const char *payload)
  746. {
  747. if (!url || !headers || !payload)
  748. {
  749. FURI_LOG_E("FlipperHTTP", "Invalid arguments provided to flipper_http_delete_request_with_headers.");
  750. return false;
  751. }
  752. // Prepare DELETE request command with headers and data
  753. char command[256];
  754. int ret = snprintf(command, sizeof(command), "[DELETE/HTTP]{\"url\":\"%s\",\"headers\":%s,\"payload\":%s}", url, headers, payload);
  755. if (ret < 0 || ret >= (int)sizeof(command))
  756. {
  757. FURI_LOG_E("FlipperHTTP", "Failed to format DELETE request command with headers and data.");
  758. return false;
  759. }
  760. // Send DELETE request via UART
  761. if (!flipper_http_send_data(command))
  762. {
  763. FURI_LOG_E("FlipperHTTP", "Failed to send DELETE request command with headers and data.");
  764. return false;
  765. }
  766. // The response will be handled asynchronously via the callback
  767. return true;
  768. }
  769. // Function to handle received data asynchronously
  770. /**
  771. * @brief Callback function to handle received data asynchronously.
  772. * @return void
  773. * @param line The received line.
  774. * @param context The context passed to the callback.
  775. * @note The received data will be handled asynchronously via the callback and handles the state of the UART.
  776. */
  777. void flipper_http_rx_callback(const char *line, void *context)
  778. {
  779. if (!line || !context)
  780. {
  781. FURI_LOG_E(HTTP_TAG, "Invalid arguments provided to flipper_http_rx_callback.");
  782. return;
  783. }
  784. // Trim the received line to check if it's empty
  785. char *trimmed_line = trim(line);
  786. if (trimmed_line != NULL && trimmed_line[0] != '\0')
  787. {
  788. fhttp.last_response = (char *)line;
  789. }
  790. free(trimmed_line); // Free the allocated memory for trimmed_line
  791. if (fhttp.state != INACTIVE && fhttp.state != ISSUE)
  792. {
  793. fhttp.state = RECEIVING;
  794. }
  795. // Uncomment below line to log the data received over UART
  796. //(HTTP_TAG, "Received UART line: %s", line);
  797. // Check if we've started receiving data from a GET request
  798. if (fhttp.started_receiving_get)
  799. {
  800. // Restart the timeout timer each time new data is received
  801. furi_timer_restart(fhttp.get_timeout_timer, TIMEOUT_DURATION_TICKS);
  802. if (strstr(line, "[GET/END]") != NULL)
  803. {
  804. FURI_LOG_I(HTTP_TAG, "GET request completed.");
  805. // Stop the timer since we've completed the GET request
  806. furi_timer_stop(fhttp.get_timeout_timer);
  807. if (fhttp.received_data)
  808. {
  809. // uncomment if you want to save the received data to the external storage
  810. // flipper_http_save_received_data(strlen(fhttp.received_data), fhttp.received_data);
  811. fhttp.started_receiving_get = false;
  812. fhttp.just_started_get = false;
  813. fhttp.state = IDLE;
  814. return;
  815. }
  816. else
  817. {
  818. FURI_LOG_E(HTTP_TAG, "No data received.");
  819. fhttp.started_receiving_get = false;
  820. fhttp.just_started_get = false;
  821. fhttp.state = IDLE;
  822. return;
  823. }
  824. }
  825. // Append the new line to the existing data
  826. if (fhttp.received_data == NULL)
  827. {
  828. fhttp.received_data = (char *)malloc(strlen(line) + 2); // +2 for newline and null terminator
  829. if (fhttp.received_data)
  830. {
  831. strcpy(fhttp.received_data, line);
  832. fhttp.received_data[strlen(line)] = '\n'; // Add newline
  833. fhttp.received_data[strlen(line) + 1] = '\0'; // Null terminator
  834. }
  835. }
  836. else
  837. {
  838. size_t current_len = strlen(fhttp.received_data);
  839. size_t new_size = current_len + strlen(line) + 2; // +2 for newline and null terminator
  840. fhttp.received_data = (char *)realloc(fhttp.received_data, new_size);
  841. if (fhttp.received_data)
  842. {
  843. memcpy(fhttp.received_data + current_len, line, strlen(line)); // Copy line at the end of the current data
  844. fhttp.received_data[current_len + strlen(line)] = '\n'; // Add newline
  845. fhttp.received_data[current_len + strlen(line) + 1] = '\0'; // Null terminator
  846. }
  847. }
  848. if (!fhttp.just_started_get)
  849. {
  850. fhttp.just_started_get = true;
  851. }
  852. return;
  853. }
  854. // Check if we've started receiving data from a POST request
  855. else if (fhttp.started_receiving_post)
  856. {
  857. // Restart the timeout timer each time new data is received
  858. furi_timer_restart(fhttp.get_timeout_timer, TIMEOUT_DURATION_TICKS);
  859. if (strstr(line, "[POST/END]") != NULL)
  860. {
  861. FURI_LOG_I(HTTP_TAG, "POST request completed.");
  862. // Stop the timer since we've completed the POST request
  863. furi_timer_stop(fhttp.get_timeout_timer);
  864. if (fhttp.received_data)
  865. {
  866. // uncomment if you want to save the received data to the external storage
  867. // flipper_http_save_received_data(strlen(fhttp.received_data), fhttp.received_data);
  868. fhttp.started_receiving_post = false;
  869. fhttp.just_started_post = false;
  870. fhttp.state = IDLE;
  871. return;
  872. }
  873. else
  874. {
  875. FURI_LOG_E(HTTP_TAG, "No data received.");
  876. fhttp.started_receiving_post = false;
  877. fhttp.just_started_post = false;
  878. fhttp.state = IDLE;
  879. return;
  880. }
  881. }
  882. // Append the new line to the existing data
  883. if (fhttp.received_data == NULL)
  884. {
  885. fhttp.received_data = (char *)malloc(strlen(line) + 2); // +2 for newline and null terminator
  886. if (fhttp.received_data)
  887. {
  888. strcpy(fhttp.received_data, line);
  889. fhttp.received_data[strlen(line)] = '\n'; // Add newline
  890. fhttp.received_data[strlen(line) + 1] = '\0'; // Null terminator
  891. }
  892. }
  893. else
  894. {
  895. size_t current_len = strlen(fhttp.received_data);
  896. size_t new_size = current_len + strlen(line) + 2; // +2 for newline and null terminator
  897. fhttp.received_data = (char *)realloc(fhttp.received_data, new_size);
  898. if (fhttp.received_data)
  899. {
  900. memcpy(fhttp.received_data + current_len, line, strlen(line)); // Copy line at the end of the current data
  901. fhttp.received_data[current_len + strlen(line)] = '\n'; // Add newline
  902. fhttp.received_data[current_len + strlen(line) + 1] = '\0'; // Null terminator
  903. }
  904. }
  905. if (!fhttp.just_started_post)
  906. {
  907. fhttp.just_started_post = true;
  908. }
  909. return;
  910. }
  911. // Check if we've started receiving data from a PUT request
  912. else if (fhttp.started_receiving_put)
  913. {
  914. // Restart the timeout timer each time new data is received
  915. furi_timer_restart(fhttp.get_timeout_timer, TIMEOUT_DURATION_TICKS);
  916. if (strstr(line, "[PUT/END]") != NULL)
  917. {
  918. FURI_LOG_I(HTTP_TAG, "PUT request completed.");
  919. // Stop the timer since we've completed the PUT request
  920. furi_timer_stop(fhttp.get_timeout_timer);
  921. if (fhttp.received_data)
  922. {
  923. // uncomment if you want to save the received data to the external storage
  924. // flipper_http_save_received_data(strlen(fhttp.received_data), fhttp.received_data);
  925. fhttp.started_receiving_put = false;
  926. fhttp.just_started_put = false;
  927. fhttp.state = IDLE;
  928. return;
  929. }
  930. else
  931. {
  932. FURI_LOG_E(HTTP_TAG, "No data received.");
  933. fhttp.started_receiving_put = false;
  934. fhttp.just_started_put = false;
  935. fhttp.state = IDLE;
  936. return;
  937. }
  938. }
  939. // Append the new line to the existing data
  940. if (fhttp.received_data == NULL)
  941. {
  942. fhttp.received_data = (char *)malloc(strlen(line) + 2); // +2 for newline and null terminator
  943. if (fhttp.received_data)
  944. {
  945. strcpy(fhttp.received_data, line);
  946. fhttp.received_data[strlen(line)] = '\n'; // Add newline
  947. fhttp.received_data[strlen(line) + 1] = '\0'; // Null terminator
  948. }
  949. }
  950. else
  951. {
  952. size_t current_len = strlen(fhttp.received_data);
  953. size_t new_size = current_len + strlen(line) + 2; // +2 for newline and null terminator
  954. fhttp.received_data = (char *)realloc(fhttp.received_data, new_size);
  955. if (fhttp.received_data)
  956. {
  957. memcpy(fhttp.received_data + current_len, line, strlen(line)); // Copy line at the end of the current data
  958. fhttp.received_data[current_len + strlen(line)] = '\n'; // Add newline
  959. fhttp.received_data[current_len + strlen(line) + 1] = '\0'; // Null terminator
  960. }
  961. }
  962. if (!fhttp.just_started_put)
  963. {
  964. fhttp.just_started_put = true;
  965. }
  966. return;
  967. }
  968. // Check if we've started receiving data from a DELETE request
  969. else if (fhttp.started_receiving_delete)
  970. {
  971. // Restart the timeout timer each time new data is received
  972. furi_timer_restart(fhttp.get_timeout_timer, TIMEOUT_DURATION_TICKS);
  973. if (strstr(line, "[DELETE/END]") != NULL)
  974. {
  975. FURI_LOG_I(HTTP_TAG, "DELETE request completed.");
  976. // Stop the timer since we've completed the DELETE request
  977. furi_timer_stop(fhttp.get_timeout_timer);
  978. if (fhttp.received_data)
  979. {
  980. // uncomment if you want to save the received data to the external storage
  981. // flipper_http_save_received_data(strlen(fhttp.received_data), fhttp.received_data);
  982. fhttp.started_receiving_delete = false;
  983. fhttp.just_started_delete = false;
  984. fhttp.state = IDLE;
  985. return;
  986. }
  987. else
  988. {
  989. FURI_LOG_E(HTTP_TAG, "No data received.");
  990. fhttp.started_receiving_delete = false;
  991. fhttp.just_started_delete = false;
  992. fhttp.state = IDLE;
  993. return;
  994. }
  995. }
  996. // Append the new line to the existing data
  997. if (fhttp.received_data == NULL)
  998. {
  999. fhttp.received_data = (char *)malloc(strlen(line) + 2); // +2 for newline and null terminator
  1000. if (fhttp.received_data)
  1001. {
  1002. strcpy(fhttp.received_data, line);
  1003. fhttp.received_data[strlen(line)] = '\n'; // Add newline
  1004. fhttp.received_data[strlen(line) + 1] = '\0'; // Null terminator
  1005. }
  1006. }
  1007. else
  1008. {
  1009. size_t current_len = strlen(fhttp.received_data);
  1010. size_t new_size = current_len + strlen(line) + 2; // +2 for newline and null terminator
  1011. fhttp.received_data = (char *)realloc(fhttp.received_data, new_size);
  1012. if (fhttp.received_data)
  1013. {
  1014. memcpy(fhttp.received_data + current_len, line, strlen(line)); // Copy line at the end of the current data
  1015. fhttp.received_data[current_len + strlen(line)] = '\n'; // Add newline
  1016. fhttp.received_data[current_len + strlen(line) + 1] = '\0'; // Null terminator
  1017. }
  1018. }
  1019. if (!fhttp.just_started_delete)
  1020. {
  1021. fhttp.just_started_delete = true;
  1022. }
  1023. return;
  1024. }
  1025. // Handle different types of responses
  1026. if (strstr(line, "[SUCCESS]") != NULL || strstr(line, "[CONNECTED]") != NULL)
  1027. {
  1028. // FURI_LOG_I(HTTP_TAG, "Operation succeeded.");
  1029. }
  1030. else if (strstr(line, "[INFO]") != NULL)
  1031. {
  1032. FURI_LOG_I(HTTP_TAG, "Received info: %s", line);
  1033. if (fhttp.state == INACTIVE && strstr(line, "[INFO] Already connected to Wifi.") != NULL)
  1034. {
  1035. fhttp.state = IDLE;
  1036. }
  1037. }
  1038. else if (strstr(line, "[GET/SUCCESS]") != NULL)
  1039. {
  1040. FURI_LOG_I(HTTP_TAG, "GET request succeeded.");
  1041. fhttp.started_receiving_get = true;
  1042. furi_timer_start(fhttp.get_timeout_timer, TIMEOUT_DURATION_TICKS);
  1043. fhttp.state = RECEIVING;
  1044. fhttp.received_data = NULL;
  1045. return;
  1046. }
  1047. else if (strstr(line, "[POST/SUCCESS]") != NULL)
  1048. {
  1049. FURI_LOG_I(HTTP_TAG, "POST request succeeded.");
  1050. fhttp.started_receiving_post = true;
  1051. furi_timer_start(fhttp.get_timeout_timer, TIMEOUT_DURATION_TICKS);
  1052. fhttp.state = RECEIVING;
  1053. fhttp.received_data = NULL;
  1054. return;
  1055. }
  1056. else if (strstr(line, "[PUT/SUCCESS]") != NULL)
  1057. {
  1058. FURI_LOG_I(HTTP_TAG, "PUT request succeeded.");
  1059. fhttp.started_receiving_put = true;
  1060. furi_timer_start(fhttp.get_timeout_timer, TIMEOUT_DURATION_TICKS);
  1061. fhttp.state = RECEIVING;
  1062. fhttp.received_data = NULL;
  1063. return;
  1064. }
  1065. else if (strstr(line, "[DELETE/SUCCESS]") != NULL)
  1066. {
  1067. FURI_LOG_I(HTTP_TAG, "DELETE request succeeded.");
  1068. fhttp.started_receiving_delete = true;
  1069. furi_timer_start(fhttp.get_timeout_timer, TIMEOUT_DURATION_TICKS);
  1070. fhttp.state = RECEIVING;
  1071. fhttp.received_data = NULL;
  1072. return;
  1073. }
  1074. else if (strstr(line, "[DISCONNECTED]") != NULL)
  1075. {
  1076. FURI_LOG_I(HTTP_TAG, "WiFi disconnected successfully.");
  1077. }
  1078. else if (strstr(line, "[ERROR]") != NULL)
  1079. {
  1080. FURI_LOG_E(HTTP_TAG, "Received error: %s", line);
  1081. fhttp.state = ISSUE;
  1082. return;
  1083. }
  1084. else if (strstr(line, "[PONG]") != NULL)
  1085. {
  1086. FURI_LOG_I(HTTP_TAG, "Received PONG response: Wifi Dev Board is still alive.");
  1087. // send command to connect to WiFi
  1088. if (fhttp.state == INACTIVE)
  1089. {
  1090. fhttp.state = IDLE;
  1091. return;
  1092. }
  1093. }
  1094. if (fhttp.state == INACTIVE && strstr(line, "[PONG]") != NULL)
  1095. {
  1096. fhttp.state = IDLE;
  1097. }
  1098. else if (fhttp.state == INACTIVE && strstr(line, "[PONG]") == NULL)
  1099. {
  1100. fhttp.state = INACTIVE;
  1101. }
  1102. else
  1103. {
  1104. fhttp.state = IDLE;
  1105. }
  1106. }
  1107. // Function to save received data to a file
  1108. /**
  1109. * @brief Save the received data to a file.
  1110. * @return true if the data was saved successfully, false otherwise.
  1111. * @param bytes_received The number of bytes received.
  1112. * @param line_buffer The buffer containing the received data.
  1113. * @note The data will be saved to a file in the STORAGE_EXT_PATH_PREFIX "/apps_data/" http_tag "/received_data.txt" directory.
  1114. */
  1115. bool flipper_http_save_received_data(size_t bytes_received, const char line_buffer[])
  1116. {
  1117. const char *output_file_path = STORAGE_EXT_PATH_PREFIX "/apps_data/" http_tag "/received_data.txt";
  1118. // Ensure the directory exists
  1119. char directory_path[128];
  1120. snprintf(directory_path, sizeof(directory_path), STORAGE_EXT_PATH_PREFIX "/apps_data/" http_tag);
  1121. Storage *_storage = NULL;
  1122. File *_file = NULL;
  1123. // Open the storage if not opened already
  1124. // Initialize storage and create the directory if it doesn't exist
  1125. _storage = furi_record_open(RECORD_STORAGE);
  1126. storage_common_mkdir(_storage, directory_path); // Create directory if it doesn't exist
  1127. _file = storage_file_alloc(_storage);
  1128. // Open file for writing and append data line by line
  1129. if (!storage_file_open(_file, output_file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS))
  1130. {
  1131. FURI_LOG_E(HTTP_TAG, "Failed to open output file for writing.");
  1132. storage_file_free(_file);
  1133. furi_record_close(RECORD_STORAGE);
  1134. return false;
  1135. }
  1136. // Write each line received from the UART to the file
  1137. if (bytes_received > 0 && _file)
  1138. {
  1139. storage_file_write(_file, line_buffer, bytes_received);
  1140. storage_file_write(_file, "\n", 1); // Add a newline after each line
  1141. }
  1142. else
  1143. {
  1144. FURI_LOG_E(HTTP_TAG, "No data received.");
  1145. return false;
  1146. }
  1147. if (_file)
  1148. {
  1149. storage_file_close(_file);
  1150. storage_file_free(_file);
  1151. _file = NULL;
  1152. }
  1153. if (_storage)
  1154. {
  1155. furi_record_close(RECORD_STORAGE);
  1156. _storage = NULL;
  1157. }
  1158. return true;
  1159. }
  1160. // Function to trim leading and trailing spaces and newlines from a constant string
  1161. char *trim(const char *str)
  1162. {
  1163. const char *end;
  1164. char *trimmed_str;
  1165. size_t len;
  1166. // Trim leading space
  1167. while (isspace((unsigned char)*str))
  1168. str++;
  1169. // All spaces?
  1170. if (*str == 0)
  1171. return strdup(""); // Return an empty string if all spaces
  1172. // Trim trailing space
  1173. end = str + strlen(str) - 1;
  1174. while (end > str && isspace((unsigned char)*end))
  1175. end--;
  1176. // Set length for the trimmed string
  1177. len = end - str + 1;
  1178. // Allocate space for the trimmed string and null terminator
  1179. trimmed_str = (char *)malloc(len + 1);
  1180. if (trimmed_str == NULL)
  1181. {
  1182. return NULL; // Handle memory allocation failure
  1183. }
  1184. // Copy the trimmed part of the string into trimmed_str
  1185. strncpy(trimmed_str, str, len);
  1186. trimmed_str[len] = '\0'; // Null terminate the string
  1187. return trimmed_str;
  1188. }
  1189. #endif // FLIPPER_HTTP_H