API Client.c
API Client.c
c
1. General: Inconsistent TAG Definition
Problem:
The TAG macro is defined globally as FILE_TAG, then undefined and redefined
locally within each function. This practice is unusual and can make debugging
less clear if FILE_TAG is intended for file-wide logging and specific functions
need more granular tags.
Suggestion:
If each function requires a unique tag, declare a static const char* TAG =
"function_name"; at the top of each function. If FILE_TAG is sufficient for the
entire file, remove the local #undef TAG and #define TAG directives.
Code Snippet (Example for function-specific TAG):
C
// Remove: #define FILE_TAG "api_client" at the top if you go this route
// Inside api_client_init
static const char* TAG = "api_client.init"; // Define TAG locally for the function
// ... rest of the function ...
// Inside api_client_get_remote_config
static const char* TAG = "api_client.get_config"; // Define TAG locally for the function
// ... rest of the function ...
2. _http_event_handler: Advanced TLS Error Handling
Problem:
The HTTP_EVENT_DISCONNECTED block attempts to get and clear the last TLS
error using evt->data cast to esp_tls_error_handle_t. While evt->data might
contain a TLS error handle in some disconnection scenarios, this isn't always
guaranteed or the most robust way to get TLS errors, as the data field's
meaning can vary by event. Relying on this without clearer documentation or
understanding of evt->data for all disconnection types could be brittle.
Suggestion:
For most applications, the error returned by esp_http_client_perform or
esp_http_client_read is sufficient. If detailed TLS error debugging is required,
ensure that the evt->data field at HTTP_EVENT_DISCONNECTED reliably points
to a valid TLS error handle. Otherwise, simplify the logging.
Code Snippet (Simplified HTTP_EVENT_DISCONNECTED handler if deep
TLS error inspection is not critical there):
C
// Inside _http_event_handler
case HTTP_EVENT_DISCONNECTED:
ESP_LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
// Removed specific esp_tls_get_and_clear_last_error call here
// as primary error checking occurs at esp_http_client_perform/read calls.
break;
3. api_client_init: Total Request Timeout Consideration
Problem:
The timeout_ms in esp_http_client_config_t is set to
CONFIG_ESP_HTTP_CLIENT_EVENT_POST_TIMEOUT. This parameter defines the
timeout for individual read/write operations or connection attempts. It does not
set a total timeout for the entire HTTP request operation (e.g., from connection
to final response).
Suggestion:
If a total timeout for the entire request is desired, use
esp_http_client_set_timeout_ms() after esp_http_client_init() to specify it.
Ensure CONFIG_ESP_HTTP_CLIENT_EVENT_POST_TIMEOUT is appropriate for
per-operation timeouts.
Code Snippet (Example of setting total timeout):
C
// Inside api_client_init
client = esp_http_client_init(&config);
if (client == NULL) {
ESP_LOGE(TAG, "Failed to initialize HTTP client");
return ESP_FAIL;
}
int http_status = 0;
bool file_opened_for_read = false; // Track if data file was opened for reading
bool http_connection_opened = false; // Track if HTTP connection was successfully
opened
// --- Get File Size ---
struct stat st;
if (stat(SENSOR_DATA_FILE, &st) == 0) {
file_size = st.st_size;
} else {
if (errno == ENOENT) {
ESP_LOGI(TAG, "Sensor data file '%s' not found. Nothing to send.",
SENSOR_DATA_FILE);
return ESP_OK; // No file, no data to send, not an error.
} else {
ESP_LOGE(TAG, "Failed to get status for file '%s'. errno: %d (%s)",
SENSOR_DATA_FILE, errno, strerror(errno));
return ESP_FAIL;
}
}
if (file_size <= 0) {
ESP_LOGI(TAG, "File '%s' is empty or zero size. Ensuring it exists and is empty.",
SENSOR_DATA_FILE);
FILE* temp_file_for_truncate = fopen(SENSOR_DATA_FILE, "w"); // Use temporary
handle for truncation
if (temp_file_for_truncate) {
fclose(temp_file_for_truncate);
ESP_LOGI(TAG, "Successfully ensured file '%s' is empty.", SENSOR_DATA_FILE);
} else {
ESP_LOGW(TAG, "Could not open file '%s' for truncation/creation.",
SENSOR_DATA_FILE);
}
return ESP_OK;
}
ESP_LOGI(TAG, "File '%s' size: %ld bytes.", SENSOR_DATA_FILE, file_size);
cleanup:
// Free all allocated heap buffers. Always call free, as free(NULL) is safe.
free(url); url = NULL;
free(encoded_device_id); encoded_device_id = NULL;
free(read_buffer); read_buffer = NULL;
// free(response_buffer); response_buffer = NULL; // Removed as not used here.
// Close file if it was opened for reading and still holds a valid pointer
if (file_opened_for_read && file != NULL) {
fclose(file);
}
// Close HTTP connection if it was successfully opened
if (http_connection_opened) {
esp_http_client_close(client);
}
// Clean up headers and user data
esp_http_client_delete_header(client, "Content-Type");
// esp_http_client_delete_header(client, "Content-Length"); // Usually redundant
esp_http_client_set_user_data(client, NULL); // Crucial to clear if it was set for this request
if (err == ESP_OK) {
ESP_LOGI(TAG, "Post sensor data finished successfully.");
} else {
ESP_LOGE(TAG, "Post sensor data failed (err=%d). File not truncated if failure
occurred before truncation.", err);
}
return err;
}
Other improvements
// Usage in api_client_get_remote_config()
esp_err_t api_client_get_remote_config(remote_config_t* remote_config) {
char* response_buffer = NULL;
cJSON* outer_json = NULL;
char* data_json_string = NULL;
// Allocations
response_buffer = malloc(HTTP_READ_BUFFER_SIZE);
if (!response_buffer) {
CLEANUP_AND_RETURN((ESP_LOGE(TAG, "Alloc failed")), ESP_ERR_NO_MEM);
}
outer_json = cJSON_Parse(response_buffer);
if (!outer_json) {
CLEANUP_AND_RETURN((
free(response_buffer),
ESP_FAIL
);
}
---
if (err == ESP_OK) {
int status = esp_http_client_get_status_code(client);
if (status >= 200 && status < 300) {
return ESP_OK;
}
}
return err;
}
---
if (rename(temp_filename, filename) != 0) {
ESP_LOGE(TAG, "Failed to rename temp file");
unlink(temp_filename);
return ESP_FAIL;
}
return ESP_OK;
}
---
output[j] = '\0';
return ESP_OK;
}
---
---
va_start(args, format);
vsnprintf(buffer, buffer_size, format, args);
va_end(args);
return ESP_OK;
}
// Usage:
char url[300];
if (safe_snprintf(url, sizeof(url), "%s/device/%s", base_url, endpoint) != ESP_OK) {
// Handle error
}
---
free(error_buf);
}
}
These snippets address the major issues while maintaining the original code style and ESP-IDF
conventions. Each one can be integrated into the existing codebase with minimal changes to the
surrounding code.