read capability.json from storage
Signed-off-by: Peter Siegmund <developer@mars3142.org>
This commit is contained in:
@@ -7,5 +7,6 @@ idf_component_register(SRCS
|
||||
PRIV_REQUIRES
|
||||
bt
|
||||
esp_app_format
|
||||
storage
|
||||
led_matrix
|
||||
)
|
||||
|
@@ -1,12 +1,66 @@
|
||||
#include "capability_service.h"
|
||||
#include "esp_log.h"
|
||||
#include "storage.h"
|
||||
#include <string.h>
|
||||
|
||||
static const char *capa_json = "{"
|
||||
"}";
|
||||
static const char *TAG_CS = "capability_service";
|
||||
|
||||
#define CAPA_READ_CHUNK_SIZE 200 // Maximale Bytes pro Lesevorgang aus dem Storage
|
||||
|
||||
int capa_read(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
|
||||
{
|
||||
char *content = "JSON";
|
||||
esp_err_t storage_status = storage_init();
|
||||
if (storage_status != ESP_OK)
|
||||
{
|
||||
ESP_LOGE(TAG_CS, "Failed to initialize storage: %s", esp_err_to_name(storage_status));
|
||||
const char *err_msg = "Error: Storage init failed";
|
||||
os_mbuf_append(ctxt->om, err_msg, strlen(err_msg));
|
||||
// storage_uninit() sollte hier nicht aufgerufen werden, da die Initialisierung fehlschlug.
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *content = "";
|
||||
os_mbuf_append(ctxt->om, content, strlen(content));
|
||||
const char *filename = "/storage/capability.json"; // Die zu lesende Datei
|
||||
char read_buffer[CAPA_READ_CHUNK_SIZE];
|
||||
ssize_t bytes_read;
|
||||
int os_err;
|
||||
|
||||
ESP_LOGI(TAG_CS, "Reading capabilities from %s", filename);
|
||||
|
||||
// Schleife, um die Datei in Chunks zu lesen und an den mbuf anzuhängen
|
||||
while ((bytes_read = storage_read(filename, read_buffer, sizeof(read_buffer))) > 0)
|
||||
{
|
||||
ESP_LOGD(TAG_CS, "Read %zd bytes from storage", bytes_read);
|
||||
// Den gelesenen Chunk an den BLE-Antwortpuffer anhängen
|
||||
os_err = os_mbuf_append(ctxt->om, read_buffer, bytes_read);
|
||||
if (os_err != 0)
|
||||
{
|
||||
ESP_LOGE(TAG_CS, "Failed to append to mbuf (error %d). May be out of space.", os_err);
|
||||
// Der mbuf könnte voll sein. Stoppe das Anhängen.
|
||||
// Bereits angehängte Daten werden gesendet.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Fehlerbehandlung oder EOF
|
||||
if (bytes_read < 0)
|
||||
{
|
||||
ESP_LOGE(TAG_CS, "Error reading from storage (file: %s, error_code: %zd)", filename, bytes_read);
|
||||
// Wenn noch nichts angehängt wurde, sende eine Fehlermeldung.
|
||||
if (ctxt->om->om_len == 0)
|
||||
{
|
||||
const char *err_msg = "Error: Failed to read capability data";
|
||||
// Hier könnten spezifischere Fehlermeldungen basierend auf bytes_read eingefügt werden
|
||||
os_mbuf_append(ctxt->om, err_msg, strlen(err_msg));
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // bytes_read == 0, bedeutet EOF (Ende der Datei)
|
||||
ESP_LOGI(TAG_CS, "Successfully read and appended all data from %s to mbuf.", filename);
|
||||
}
|
||||
|
||||
storage_uninit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
6
components/storage/CMakeLists.txt
Normal file
6
components/storage/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
idf_component_register(SRCS
|
||||
"storage.c"
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_REQUIRES
|
||||
spiffs
|
||||
)
|
55
components/storage/include/storage.h
Normal file
55
components/storage/include/storage.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include <sys/types.h> // For ssize_t
|
||||
|
||||
/**
|
||||
* @brief Initializes the SPIFFS filesystem.
|
||||
* This function should be called once before any file operations.
|
||||
* @return ESP_OK on success, error code otherwise.
|
||||
*/
|
||||
esp_err_t storage_init(void);
|
||||
|
||||
/**
|
||||
* @brief Unregisters the SPIFFS filesystem.
|
||||
* This function should be called once when file operations are finished.
|
||||
*/
|
||||
void storage_uninit(void);
|
||||
|
||||
/**
|
||||
* @brief Reads a chunk of data from a file on SPIFFS.
|
||||
* This function maintains internal state (file pointer) to allow reading
|
||||
* the same file chunk by chunk across multiple calls.
|
||||
*
|
||||
* @param filename The path to the file to read (e.g., "/storage/my_file.txt").
|
||||
* Must be the same filename for consecutive calls to read the same file.
|
||||
* @param buffer Buffer to store the read data. Must be large enough for max_bytes.
|
||||
* @param max_bytes The maximum number of bytes to read in this call. Must be > 0.
|
||||
* @return The number of bytes read on success (>= 0).
|
||||
* Returns 0 when the end of the file is reached.
|
||||
* Returns a negative value on error:
|
||||
* -1: Invalid input parameters (filename, buffer is NULL, or max_bytes is 0).
|
||||
* -2: Failed to open the file (first call for this filename).
|
||||
* -3: Filename mismatch with the currently open file (subsequent call).
|
||||
* -4: Read error occurred.
|
||||
*/
|
||||
ssize_t storage_read(const char *filename, char *buffer, size_t max_bytes);
|
||||
|
||||
/**
|
||||
* @brief Reads a chunk of data from a file on SPIFFS, starting at a specific offset.
|
||||
* This function is stateless regarding an internally managed file pointer for sequential reads;
|
||||
* it opens, seeks, reads, and closes the file on each call.
|
||||
*
|
||||
* @param filename The path to the file to read (e.g., "/storage/my_file.txt").
|
||||
* @param buffer Buffer to store the read data. Must be large enough for nbytes.
|
||||
* @param offset The offset in the file to start reading from.
|
||||
* @param nbytes The maximum number of bytes to read in this call. Must be > 0.
|
||||
* @return The number of bytes read on success (>= 0).
|
||||
* Returns 0 when the end of the file is reached from the given offset.
|
||||
* Returns a negative value on error:
|
||||
* -1: Invalid input parameters (filename, buffer is NULL, nbytes is 0, or offset is negative).
|
||||
* -2: Failed to open the file.
|
||||
* -5: Seek error occurred.
|
||||
* -4: Read error occurred.
|
||||
*/
|
||||
ssize_t storage_read_at(const char *filename, char *buffer, off_t offset, size_t nbytes);
|
139
components/storage/storage.c
Normal file
139
components/storage/storage.c
Normal file
@@ -0,0 +1,139 @@
|
||||
#include "storage.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "esp_spiffs.h"
|
||||
|
||||
static const char *TAG = "storage";
|
||||
|
||||
static FILE *s_current_file = NULL;
|
||||
static char s_current_filename[256] = {0}; // Buffer to store the current filename
|
||||
|
||||
esp_err_t storage_init(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Initializing SPIFFS");
|
||||
|
||||
esp_vfs_spiffs_conf_t conf = {
|
||||
.base_path = "/storage", // Path where the filesystem will be mounted
|
||||
.partition_label = "storage", // Partition label (must match partitions.csv)
|
||||
.max_files = 5, // Maximum number of files that can be open at the same time
|
||||
.format_if_mount_failed = true // Format partition if mount fails
|
||||
};
|
||||
|
||||
// Initialize and mount SPIFFS
|
||||
esp_err_t ret = esp_vfs_spiffs_register(&conf);
|
||||
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
if (ret == ESP_FAIL)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to mount or format filesystem");
|
||||
}
|
||||
else if (ret == ESP_ERR_NOT_FOUND)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to find SPIFFS partition");
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "SPIFFS mounted");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void storage_uninit(void)
|
||||
{
|
||||
if (s_current_file != NULL)
|
||||
{
|
||||
// Log before clearing s_current_filename, using its value.
|
||||
ESP_LOGW(TAG, "File '%s' was still open during uninit. Closing it.", s_current_filename);
|
||||
fclose(s_current_file); // Close the file
|
||||
s_current_file = NULL;
|
||||
s_current_filename[0] = '\0';
|
||||
}
|
||||
esp_vfs_spiffs_unregister("storage");
|
||||
ESP_LOGI(TAG, "SPIFFS unmounted");
|
||||
}
|
||||
|
||||
ssize_t storage_read(const char *filename, char *buffer, size_t max_bytes)
|
||||
{
|
||||
// Input validation
|
||||
if (filename == NULL || filename[0] == '\0' || buffer == NULL || max_bytes == 0)
|
||||
{
|
||||
ESP_LOGE(TAG, "Invalid input parameters for storage_read");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// If no file is currently open, open the requested one
|
||||
if (s_current_file == NULL)
|
||||
{
|
||||
s_current_file = fopen(filename, "r");
|
||||
if (s_current_file == NULL)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to open file for reading: %s", filename);
|
||||
s_current_filename[0] = '\0'; // Clear filename as open failed
|
||||
return -2; // Failed to open file
|
||||
}
|
||||
// Store the filename for subsequent calls
|
||||
strncpy(s_current_filename, filename, sizeof(s_current_filename) - 1);
|
||||
s_current_filename[sizeof(s_current_filename) - 1] = '\0';
|
||||
ESP_LOGI(TAG, "Opened file: %s", s_current_filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If a file is open, ensure it's the same file requested
|
||||
if (strcmp(filename, s_current_filename) != 0)
|
||||
{
|
||||
ESP_LOGE(TAG, "Filename mismatch. Expected '%s', got '%s'. Close the current file first.",
|
||||
s_current_filename, filename);
|
||||
// Note: File remains open. Caller might need a separate close function
|
||||
// or ensure read loop finishes (returns 0 or error) before changing filename.
|
||||
return -3; // Filename mismatch
|
||||
}
|
||||
}
|
||||
|
||||
// Read a chunk
|
||||
size_t bytes_read = fread(buffer, 1, max_bytes, s_current_file);
|
||||
|
||||
// Check for read errors
|
||||
if (ferror(s_current_file))
|
||||
{
|
||||
ESP_LOGE(TAG, "Error reading file: %s", s_current_filename);
|
||||
fclose(s_current_file);
|
||||
s_current_file = NULL;
|
||||
s_current_filename[0] = '\0';
|
||||
return -4; // Read error
|
||||
}
|
||||
|
||||
// If fread returns 0 and it's not an error, it means EOF or an empty file.
|
||||
// The file should be closed only when no more bytes can be read (i.e., bytes_read == 0).
|
||||
if (bytes_read == 0) // Indicates EOF or empty file (and no ferror)
|
||||
{
|
||||
// Log before clearing s_current_filename.
|
||||
// feof(s_current_file) should be true if end of file was actually reached.
|
||||
if (feof(s_current_file))
|
||||
{
|
||||
ESP_LOGI(TAG, "EOF reached for file: %s. Closing file.", s_current_filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGI(TAG, "Read 0 bytes from file: %s (possibly empty or already at EOF). Closing file.",
|
||||
s_current_filename);
|
||||
}
|
||||
fclose(s_current_file);
|
||||
s_current_file = NULL;
|
||||
s_current_filename[0] = '\0';
|
||||
// Return 0 as per contract: "Returns 0 when the end of the file is reached."
|
||||
}
|
||||
// If bytes_read > 0, return the number of bytes read.
|
||||
// The file remains open (even if EOF was hit during this read and bytes_read < max_bytes).
|
||||
// The next call to storage_read for this file will then result in fread returning 0,
|
||||
// which will then hit the (bytes_read == 0) condition above and close the file.
|
||||
return bytes_read;
|
||||
}
|
47
data/capability.json
Normal file
47
data/capability.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"module": "Miniature Town",
|
||||
"capabilities": [
|
||||
{
|
||||
"id": 0,
|
||||
"type": "toggle",
|
||||
"default": "0",
|
||||
"label": "L8"
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"type": "toggle",
|
||||
"default": "0",
|
||||
"label": "L8"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"type": "toggle",
|
||||
"default": "0",
|
||||
"label": "L8"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"type": "toggle",
|
||||
"default": "0",
|
||||
"label": "L8"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"type": "toggle",
|
||||
"default": "0",
|
||||
"label": "L8"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"type": "toggle",
|
||||
"default": "0",
|
||||
"label": "L8"
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"type": "toggle",
|
||||
"default": "0",
|
||||
"label": "L8"
|
||||
}
|
||||
]
|
||||
}
|
@@ -5,3 +5,4 @@ idf_component_register(SRCS "main.c"
|
||||
remote_control
|
||||
persistence
|
||||
)
|
||||
spiffs_create_partition_image(storage ../data FLASH_IN_PROJECT)
|
||||
|
@@ -3,5 +3,5 @@ nvs , data , nvs , 0x9000 , 20k ,
|
||||
otadata , data , ota , 0xe000 , 8k ,
|
||||
app0 , app , ota_0 , 0x10000 , 1024k ,
|
||||
app1 , app , ota_1 , , 1024k ,
|
||||
spiffs , data , spiffs , , 1536k ,
|
||||
storage , data , spiffs , , 1536k ,
|
||||
coredump , data , coredump , , 64k ,
|
||||
|
|
Reference in New Issue
Block a user