Browse Source

Merge flip_weather from https://github.com/jblanked/FlipWeather

Willy-JL 1 year ago
parent
commit
3a0a32a989

+ 2 - 2
flip_weather/alloc/flip_weather_alloc.c

@@ -169,9 +169,9 @@ FlipWeatherApp* flip_weather_app_alloc() {
            "[ERROR]",
            0,
            0,
-           "Wifi Dev Board disconnected.\nPlease connect to the board.\nIf your board is connected,\nmake sure you have flashed\nyour WiFi Devboard with the\nlatest FlipperHTTP flash.",
+           "Wifi Dev Board disconnected.\nIf your board is connected,\nmake sure you have the\nlatest FlipperHTTP flash.",
            0,
-           7,
+           12,
            flip_weather_popup_callback,
            callback_to_submenu,
            &app->view_dispatcher,

BIN
flip_weather/assets/02-weather.png


+ 13 - 6
flip_weather/callback/flip_weather_callback.c

@@ -57,9 +57,9 @@ void flip_weather_handle_gps_draw(Canvas* canvas, bool show_gps_data) {
             }
         }
         // check status
-        else if(fhttp.state == ISSUE || !get_request_success || fhttp.last_response == NULL) {
+        else if(fhttp.state == ISSUE || !get_request_success) {
             flip_weather_request_error(canvas);
-        } else if(fhttp.state == IDLE && fhttp.last_response != NULL) {
+        } else if(fhttp.state == IDLE) {
             // success, draw GPS
             process_geo_location();
 
@@ -95,7 +95,7 @@ void flip_weather_view_draw_callback_weather(Canvas* canvas, void* model) {
         return;
     }
 
-    canvas_draw_str(canvas, 0, 10, "Loading Weather...");
+    canvas_draw_str(canvas, 0, 10, "Loading location data...");
     // handle geo location until it's processed and then handle weather
 
     // start the process
@@ -104,14 +104,18 @@ void flip_weather_view_draw_callback_weather(Canvas* canvas, void* model) {
     }
     // wait until geo location is processed
     if(!sent_get_request || !get_request_success || fhttp.state == RECEIVING) {
+        canvas_draw_str(canvas, 0, 22, "Receiving data...");
         return;
     }
     // get/set geo lcoation once
     if(!geo_information_processed) {
         flip_weather_handle_gps_draw(canvas, false);
+        canvas_draw_str(canvas, 0, 34, "Parsed location data.");
     }
     // start the weather process
     if(!sent_weather_request && fhttp.state == IDLE) {
+        canvas_clear(canvas);
+        canvas_draw_str(canvas, 0, 10, "Getting Weather...");
         sent_weather_request = true;
         char url[512];
         char* lattitude = lat_data + 10;
@@ -139,8 +143,9 @@ void flip_weather_view_draw_callback_weather(Canvas* canvas, void* model) {
         }
 
         // check status
-        if(fhttp.state == ISSUE || !weather_request_success || fhttp.last_response == NULL) {
+        if(fhttp.state == ISSUE || !weather_request_success) {
             flip_weather_request_error(canvas);
+            fhttp.state = ISSUE;
         } else {
             // success, draw weather
             process_weather();
@@ -190,14 +195,16 @@ void callback_submenu_choices(void* context, uint32_t index) {
     case FlipWeatherSubmenuIndexWeather:
         if(!flip_weather_handle_ip_address()) {
             view_dispatcher_switch_to_view(app->view_dispatcher, FlipWeatherViewPopupError);
+        } else {
+            view_dispatcher_switch_to_view(app->view_dispatcher, FlipWeatherViewWeather);
         }
-        view_dispatcher_switch_to_view(app->view_dispatcher, FlipWeatherViewWeather);
         break;
     case FlipWeatherSubmenuIndexGPS:
         if(!flip_weather_handle_ip_address()) {
             view_dispatcher_switch_to_view(app->view_dispatcher, FlipWeatherViewPopupError);
+        } else {
+            view_dispatcher_switch_to_view(app->view_dispatcher, FlipWeatherViewGPS);
         }
-        view_dispatcher_switch_to_view(app->view_dispatcher, FlipWeatherViewGPS);
         break;
     case FlipWeatherSubmenuIndexAbout:
         view_dispatcher_switch_to_view(app->view_dispatcher, FlipWeatherViewAbout);

+ 81 - 37
flip_weather/flipper_http/flipper_http.c

@@ -2,6 +2,7 @@
 FlipperHTTP fhttp;
 char rx_line_buffer[RX_LINE_BUFFER_SIZE];
 uint8_t file_buffer[FILE_BUFFER_SIZE];
+size_t file_buffer_len = 0;
 // Function to append received data to file
 // make sure to initialize the file path before calling this function
 bool flipper_http_append_to_file(
@@ -13,6 +14,15 @@ bool flipper_http_append_to_file(
     File* file = storage_file_alloc(storage);
 
     if(start_new_file) {
+        // Delete the file if it already exists
+        if(storage_file_exists(storage, file_path)) {
+            if(!storage_simply_remove_recursive(storage, file_path)) {
+                FURI_LOG_E(HTTP_TAG, "Failed to delete file: %s", file_path);
+                storage_file_free(file);
+                furi_record_close(RECORD_STORAGE);
+                return false;
+            }
+        }
         // Open the file in write mode
         if(!storage_file_open(file, file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
             FURI_LOG_E(HTTP_TAG, "Failed to open file for writing: %s", file_path);
@@ -131,12 +141,13 @@ FuriString* flipper_http_load_from_file(char* file_path) {
 int32_t flipper_http_worker(void* context) {
     UNUSED(context);
     size_t rx_line_pos = 0;
-    static size_t file_buffer_len = 0;
 
     while(1) {
         uint32_t events = furi_thread_flags_wait(
             WorkerEvtStop | WorkerEvtRxDone, FuriFlagWaitAny, FuriWaitForever);
-        if(events & WorkerEvtStop) break;
+        if(events & WorkerEvtStop) {
+            break;
+        }
         if(events & WorkerEvtRxDone) {
             // Continuously read from the stream buffer until it's empty
             while(!furi_stream_buffer_is_empty(fhttp.flipper_http_stream)) {
@@ -156,10 +167,14 @@ int32_t flipper_http_worker(void* context) {
                     // Write to file if buffer is full
                     if(file_buffer_len >= FILE_BUFFER_SIZE) {
                         if(!flipper_http_append_to_file(
-                               file_buffer, file_buffer_len, false, fhttp.file_path)) {
+                               file_buffer,
+                               file_buffer_len,
+                               fhttp.just_started_bytes,
+                               fhttp.file_path)) {
                             FURI_LOG_E(HTTP_TAG, "Failed to append data to file");
                         }
                         file_buffer_len = 0;
+                        fhttp.just_started_bytes = false;
                     }
                 }
 
@@ -182,36 +197,6 @@ int32_t flipper_http_worker(void* context) {
         }
     }
 
-    if(fhttp.save_bytes) {
-        // Write the remaining data to the file
-        if(file_buffer_len > 0) {
-            if(!flipper_http_append_to_file(file_buffer, file_buffer_len, false, fhttp.file_path)) {
-                FURI_LOG_E(HTTP_TAG, "Failed to append remaining data to file");
-            }
-        }
-    }
-
-    // remove [POST/END] and/or [GET/END] from the file
-    if(fhttp.save_bytes) {
-        char* end = NULL;
-        if((end = strstr(fhttp.file_path, "[POST/END]")) != NULL) {
-            *end = '\0';
-        } else if((end = strstr(fhttp.file_path, "[GET/END]")) != NULL) {
-            *end = '\0';
-        }
-    }
-
-    // remove newline from the from the end of the file
-    if(fhttp.save_bytes) {
-        char* end = NULL;
-        if((end = strstr(fhttp.file_path, "\n")) != NULL) {
-            *end = '\0';
-        }
-    }
-
-    // Reset the file buffer length
-    file_buffer_len = 0;
-
     return 0;
 }
 // Timer callback function
@@ -413,6 +398,7 @@ bool flipper_http_send_data(const char* data) {
     size_t data_length = strlen(data);
     if(data_length == 0) {
         FURI_LOG_E("FlipperHTTP", "Attempted to send empty data.");
+        fhttp.state = ISSUE;
         return false;
     }
 
@@ -440,7 +426,7 @@ bool flipper_http_send_data(const char* data) {
 
     // Uncomment below line to log the data sent over UART
     // FURI_LOG_I("FlipperHTTP", "Sent data over UART: %s", send_buffer);
-    fhttp.state = IDLE;
+    // fhttp.state = IDLE;
     return true;
 }
 
@@ -710,7 +696,7 @@ bool flipper_http_get_request(const char* url) {
     }
 
     // Prepare GET request command
-    char command[256];
+    char command[512];
     int ret = snprintf(command, sizeof(command), "[GET]%s", url);
     if(ret < 0 || ret >= (int)sizeof(command)) {
         FURI_LOG_E("FlipperHTTP", "Failed to format GET request command.");
@@ -1006,8 +992,35 @@ void flipper_http_rx_callback(const char* line, void* context) {
             fhttp.just_started_get = false;
             fhttp.state = IDLE;
             fhttp.save_bytes = false;
-            fhttp.is_bytes_request = false;
             fhttp.save_received_data = false;
+
+            if(fhttp.is_bytes_request) {
+                // Search for the binary marker `[GET/END]` in the file buffer
+                const char marker[] = "[GET/END]";
+                const size_t marker_len = sizeof(marker) - 1; // Exclude null terminator
+
+                for(size_t i = 0; i <= file_buffer_len - marker_len; i++) {
+                    // Check if the marker is found
+                    if(memcmp(&file_buffer[i], marker, marker_len) == 0) {
+                        // Remove the marker by shifting the remaining data left
+                        size_t remaining_len = file_buffer_len - (i + marker_len);
+                        memmove(&file_buffer[i], &file_buffer[i + marker_len], remaining_len);
+                        file_buffer_len -= marker_len;
+                        break;
+                    }
+                }
+
+                // If there is data left in the buffer, append it to the file
+                if(file_buffer_len > 0) {
+                    if(!flipper_http_append_to_file(
+                           file_buffer, file_buffer_len, false, fhttp.file_path)) {
+                        FURI_LOG_E(HTTP_TAG, "Failed to append data to file.");
+                    }
+                    file_buffer_len = 0;
+                }
+            }
+
+            fhttp.is_bytes_request = false;
             return;
         }
 
@@ -1041,8 +1054,35 @@ void flipper_http_rx_callback(const char* line, void* context) {
             fhttp.just_started_post = false;
             fhttp.state = IDLE;
             fhttp.save_bytes = false;
-            fhttp.is_bytes_request = false;
             fhttp.save_received_data = false;
+
+            if(fhttp.is_bytes_request) {
+                // Search for the binary marker `[POST/END]` in the file buffer
+                const char marker[] = "[POST/END]";
+                const size_t marker_len = sizeof(marker) - 1; // Exclude null terminator
+
+                for(size_t i = 0; i <= file_buffer_len - marker_len; i++) {
+                    // Check if the marker is found
+                    if(memcmp(&file_buffer[i], marker, marker_len) == 0) {
+                        // Remove the marker by shifting the remaining data left
+                        size_t remaining_len = file_buffer_len - (i + marker_len);
+                        memmove(&file_buffer[i], &file_buffer[i + marker_len], remaining_len);
+                        file_buffer_len -= marker_len;
+                        break;
+                    }
+                }
+
+                // If there is data left in the buffer, append it to the file
+                if(file_buffer_len > 0) {
+                    if(!flipper_http_append_to_file(
+                           file_buffer, file_buffer_len, false, fhttp.file_path)) {
+                        FURI_LOG_E(HTTP_TAG, "Failed to append data to file.");
+                    }
+                    file_buffer_len = 0;
+                }
+            }
+
+            fhttp.is_bytes_request = false;
             return;
         }
 
@@ -1149,6 +1189,8 @@ void flipper_http_rx_callback(const char* line, void* context) {
         fhttp.state = RECEIVING;
         // for GET request, save data only if it's a bytes request
         fhttp.save_bytes = fhttp.is_bytes_request;
+        fhttp.just_started_bytes = true;
+        file_buffer_len = 0;
         return;
     } else if(strstr(line, "[POST/SUCCESS]") != NULL) {
         FURI_LOG_I(HTTP_TAG, "POST request succeeded.");
@@ -1157,6 +1199,8 @@ void flipper_http_rx_callback(const char* line, void* context) {
         fhttp.state = RECEIVING;
         // for POST request, save data only if it's a bytes request
         fhttp.save_bytes = fhttp.is_bytes_request;
+        fhttp.just_started_bytes = true;
+        file_buffer_len = 0;
         return;
     } else if(strstr(line, "[PUT/SUCCESS]") != NULL) {
         FURI_LOG_I(HTTP_TAG, "PUT request succeeded.");

+ 4 - 1
flip_weather/flipper_http/flipper_http.h

@@ -14,7 +14,7 @@
 #define HTTP_TAG               "FlipWeather" // change this to your app name
 #define http_tag               "flip_weather" // change this to your app id
 #define UART_CH                (momentum_settings.uart_esp_channel) // UART channel
-#define TIMEOUT_DURATION_TICKS (5 * 1000) // 5 seconds
+#define TIMEOUT_DURATION_TICKS (6 * 1000) // 6 seconds
 #define BAUDRATE               (115200) // UART baudrate
 #define RX_BUF_SIZE            1024 // UART RX buffer size
 #define RX_LINE_BUFFER_SIZE    4096 // UART RX line buffer size (increase for large responses)
@@ -74,12 +74,15 @@ typedef struct {
     bool is_bytes_request; // Flag to indicate if the request is for bytes
     bool save_bytes; // Flag to save the received data to a file
     bool save_received_data; // Flag to save the received data to a file
+
+    bool just_started_bytes; // Indicates if bytes data reception has just started
 } FlipperHTTP;
 
 extern FlipperHTTP fhttp;
 // Global static array for the line buffer
 extern char rx_line_buffer[RX_LINE_BUFFER_SIZE];
 extern uint8_t file_buffer[FILE_BUFFER_SIZE];
+extern size_t file_buffer_len;
 
 // fhttp.last_response holds the last received data from the UART
 

+ 29 - 2
flip_weather/parse/flip_weather_parse.c

@@ -13,7 +13,7 @@ bool flip_weather_parse_ip_address() {
         FURI_LOG_E(TAG, "Failed to load received data from file.");
         return false;
     }
-    const char* data_cstr = furi_string_get_cstr(returned_data);
+    char* data_cstr = (char*)furi_string_get_cstr(returned_data);
     if(data_cstr == NULL) {
         FURI_LOG_E(TAG, "Failed to get C-string from FuriString.");
         furi_string_free(returned_data);
@@ -25,13 +25,14 @@ bool flip_weather_parse_ip_address() {
         sent_get_request = true;
         get_request_success = false;
         fhttp.state = ISSUE;
-        free(ip);
         furi_string_free(returned_data);
+        free(data_cstr);
         return false;
     }
     snprintf(ip_address, 16, "%s", ip);
     free(ip);
     furi_string_free(returned_data);
+    free(data_cstr);
     return true;
 }
 
@@ -105,6 +106,13 @@ void process_geo_location() {
         char* latitude = get_json_value("latitude", fhttp.last_response, MAX_TOKENS);
         char* longitude = get_json_value("longitude", fhttp.last_response, MAX_TOKENS);
 
+        if(city == NULL || region == NULL || country == NULL || latitude == NULL ||
+           longitude == NULL) {
+            FURI_LOG_E(TAG, "Failed to get geo location data");
+            fhttp.state = ISSUE;
+            return;
+        }
+
         snprintf(city_data, 64, "City: %s", city);
         snprintf(region_data, 64, "Region: %s", region);
         snprintf(country_data, 64, "Country: %s", country);
@@ -113,6 +121,11 @@ void process_geo_location() {
         snprintf(ip_data, 64, "IP Address: %s", ip_address);
 
         fhttp.state = IDLE;
+        free(city);
+        free(region);
+        free(country);
+        free(latitude);
+        free(longitude);
     }
 }
 
@@ -127,6 +140,13 @@ void process_weather() {
         char* snowfall = get_json_value("snowfall", current_data, MAX_TOKENS);
         char* time = get_json_value("time", current_data, MAX_TOKENS);
 
+        if(current_data == NULL || temperature == NULL || precipitation == NULL || rain == NULL ||
+           showers == NULL || snowfall == NULL || time == NULL) {
+            FURI_LOG_E(TAG, "Failed to get weather data");
+            fhttp.state = ISSUE;
+            return;
+        }
+
         // replace the "T" in time with a space
         char* ptr = strstr(time, "T");
         if(ptr != NULL) {
@@ -141,6 +161,13 @@ void process_weather() {
         snprintf(time_data, 64, "Time: %s", time);
 
         fhttp.state = IDLE;
+        free(current_data);
+        free(temperature);
+        free(precipitation);
+        free(rain);
+        free(showers);
+        free(snowfall);
+        free(time);
     } else if(!weather_information_processed && fhttp.last_response == NULL) {
         FURI_LOG_E(TAG, "Failed to process weather data");
         // store error message