update BLE advertisment

Signed-off-by: Peter Siegmund <mars3142@users.noreply.github.com>
This commit is contained in:
2025-08-26 22:42:56 +02:00
parent 037316a719
commit 1f0d36dc1a
22 changed files with 201 additions and 482 deletions

View File

@@ -0,0 +1,13 @@
ARG DOCKER_TAG=latest
FROM espressif/idf:${DOCKER_TAG}
ENV LC_ALL=C.UTF-8
ENV LANG=C.UTF-8
RUN apt-get update -y && apt-get install udev -y
RUN echo "source /opt/esp/idf/export.sh > /dev/null 2>&1" >> ~/.bashrc
ENTRYPOINT [ "/opt/esp/entrypoint.sh" ]
CMD ["/bin/bash", "-c"]

View File

@@ -0,0 +1,21 @@
{
"name": "ESP-IDF QEMU",
"build": {
"dockerfile": "Dockerfile"
},
"customizations": {
"vscode": {
"settings": {
"terminal.integrated.defaultProfile.linux": "bash",
"idf.espIdfPath": "/opt/esp/idf",
"idf.toolsPath": "/opt/esp",
"idf.gitPath": "/usr/bin/git"
},
"extensions": [
"espressif.esp-idf-extension",
"espressif.esp-idf-web"
]
}
},
"runArgs": ["--privileged"]
}

3
firmware/.gitignore vendored
View File

@@ -1,4 +1,4 @@
build/** build*/
managed_components/** managed_components/**
sdkconfig sdkconfig
sdkconfig.old sdkconfig.old
@@ -6,4 +6,3 @@ sdkconfig.old
.espidf.*.json .espidf.*.json
*.svd *.svd
*.lock *.lock
build-release/

View File

@@ -3,4 +3,4 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake) include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(lighthouses) project(Lighthouse)

View File

@@ -1,2 +1,13 @@
release: compile: clean
idf.py -B build-release -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.release" fullclean build idf.py -B build-release -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.release" build size
deploy: compile
idf.py -B build-release -DSDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.release" flash monitor
clean:
rm -rf build
rm -rf build-release
rm -rf sdkconfig
rm -rf dependencies.lock
.PHONY: compile deploy clean

View File

@@ -1,7 +1,6 @@
#include "persistence.h" #include "persistence.h"
#include "esp_err.h" #include "esp_err.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_mac.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/semphr.h" #include "freertos/semphr.h"
#include "nvs_flash.h" #include "nvs_flash.h"

View File

@@ -1,11 +1,9 @@
idf_component_register(SRCS idf_component_register(SRCS
"capability_service.c"
"device_service.c" "device_service.c"
"led_service.c" "light_service.c"
"remote_control.c" "remote_control.c"
INCLUDE_DIRS "include" INCLUDE_DIRS "include"
PRIV_REQUIRES PRIV_REQUIRES
bt bt
esp_app_format esp_app_format
storage
) )

View File

@@ -1,156 +0,0 @@
#include "capability_service.h"
#include "esp_log.h"
#include "storage.h"
#include <string.h>
#include <stdlib.h> // For malloc, free
#include "host/ble_hs.h" // For ble_hs_mbuf_from_flat, ble_att_mtu
#include "host/ble_uuid.h" // For BLE_ATT_MTU_DFLT (often included via ble_hs.h)
#include "nimble/nimble_port.h" // For os_mbuf related functions
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)
{
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;
}
int capa_char_1979_user_desc(uint16_t con_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
{
const char *desc = "Capabilities of the device";
os_mbuf_append(ctxt->om, desc, strlen(desc));
return 0;
}
void capa_notify_data(uint16_t conn_handle, uint16_t char_val_handle)
{
esp_err_t storage_status = storage_init();
if (storage_status != ESP_OK)
{
ESP_LOGE(TAG_CS, "Notify: Failed to initialize storage: %s", esp_err_to_name(storage_status));
return;
}
const char *filename = "/storage/capability.json";
uint16_t mtu = ble_att_mtu(conn_handle);
if (mtu == 0) { // Should not happen for an active connection, fallback
ESP_LOGW(TAG_CS, "Notify: ble_att_mtu returned 0, using default MTU %d.", BLE_ATT_MTU_DFLT);
mtu = BLE_ATT_MTU_DFLT;
}
// Max payload for notification is MTU - 3 (1 byte opcode for Notification, 2 bytes attribute handle)
size_t notify_chunk_size = (mtu > 3) ? (mtu - 3) : (BLE_ATT_MTU_DFLT - 3); // Ensure mtu > 3
// Further cap by CAPA_READ_CHUNK_SIZE if it's smaller and meant as an upper limit for any single read op
if (notify_chunk_size > CAPA_READ_CHUNK_SIZE) {
notify_chunk_size = CAPA_READ_CHUNK_SIZE;
}
if (notify_chunk_size == 0) { // Safety check
ESP_LOGE(TAG_CS, "Notify: Calculated notify_chunk_size is 0. Aborting.");
storage_uninit();
return;
}
char *read_buffer = malloc(notify_chunk_size);
if (!read_buffer)
{
ESP_LOGE(TAG_CS, "Notify: Failed to allocate read_buffer (%zu bytes)", notify_chunk_size);
storage_uninit();
return;
}
ESP_LOGI(TAG_CS, "Notify: Reading capabilities from %s to notify conn %u, attr %u (chunk size %zu, MTU %u)",
filename, conn_handle, char_val_handle, notify_chunk_size, mtu);
FILE *fp = fopen(filename, "r");
if (!fp)
{
ESP_LOGE(TAG_CS, "Notify: Failed to open %s for reading.", filename);
free(read_buffer);
storage_uninit();
return;
}
ssize_t bytes_read;
while ((bytes_read = fread(read_buffer, 1, notify_chunk_size, fp)) > 0)
{
ESP_LOGD(TAG_CS, "Notify: Read %zd bytes from storage for notification", bytes_read);
struct os_mbuf *om = ble_hs_mbuf_from_flat(read_buffer, bytes_read);
if (!om) {
ESP_LOGE(TAG_CS, "Notify: Failed to allocate mbuf for notification. Stopping.");
break; // Stop sending if mbuf allocation fails
}
int rc = ble_gatts_notify_custom(conn_handle, char_val_handle, om);
if (rc != 0) {
ESP_LOGE(TAG_CS, "Notify: Error sending notification (rc=%d). Stopping.", rc);
// ble_gatts_notify_custom frees 'om' on success or if it takes ownership even on some errors.
// If it returns an error where 'om' is not freed, we might need os_mbuf_free_chain(om);
// For now, assume NimBLE handles 'om' correctly in error cases like BLE_HS_ENOMEM.
break; // Stop if notification fails
}
ESP_LOGD(TAG_CS, "Notify: Sent %zd bytes successfully.", bytes_read);
}
if (ferror(fp)) { ESP_LOGE(TAG_CS, "Notify: File read error from %s.", filename); }
fclose(fp);
free(read_buffer);
storage_uninit();
ESP_LOGI(TAG_CS, "Notify: Finished sending capability data for conn %u.", conn_handle);
}

View File

@@ -3,21 +3,48 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
int device_model_number_read(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) int device_name_read(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
{ {
char model_number_str[65]; char firmware_revision_str[33];
const esp_app_desc_t *app_desc = esp_app_get_description(); const esp_app_desc_t *app_desc = esp_app_get_description();
if (app_desc->project_name[0] != '\0' && app_desc->version[0] != '\0') if (app_desc->project_name[0] != '\0')
{ {
snprintf(model_number_str, sizeof(model_number_str), "%s v%s", app_desc->project_name, app_desc->version); snprintf(firmware_revision_str, sizeof(firmware_revision_str), "%s", app_desc->project_name);
} }
else else
{ {
snprintf(model_number_str, sizeof(model_number_str), "undefined"); snprintf(firmware_revision_str, sizeof(firmware_revision_str), "undefined");
} }
os_mbuf_append(ctxt->om, model_number_str, strlen(model_number_str)); os_mbuf_append(ctxt->om, firmware_revision_str, strlen(firmware_revision_str));
return 0;
}
int device_firmware_revision_read(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt,
void *arg)
{
char firmware_revision_str[33];
const esp_app_desc_t *app_desc = esp_app_get_description();
if (app_desc->version[0] != '\0')
{
snprintf(firmware_revision_str, sizeof(firmware_revision_str), "%s", app_desc->version);
}
else
{
snprintf(firmware_revision_str, sizeof(firmware_revision_str), "undefined");
}
os_mbuf_append(ctxt->om, firmware_revision_str, strlen(firmware_revision_str));
return 0;
}
int device_hardware_revision_read(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt,
void *arg)
{
char *hardware_revision = "rev1";
os_mbuf_append(ctxt->om, hardware_revision, strlen(hardware_revision));
return 0; return 0;
} }

View File

@@ -1,11 +0,0 @@
#pragma once
#include "host/ble_hs.h"
#include <stdio.h>
/// Service Characteristics Callback
int capa_read(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);
void capa_notify_data(uint16_t conn_handle, uint16_t char_val_handle);
/// Service Characteristics User Description
int capa_char_1979_user_desc(uint16_t con_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);

View File

@@ -3,5 +3,16 @@
#include "host/ble_hs.h" #include "host/ble_hs.h"
#include <stdio.h> #include <stdio.h>
int device_model_number_read(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg); // 0x2A00 Device Name
int device_name_read(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);
// 0x2A26 Firmware Revision String
int device_firmware_revision_read(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt,
void *arg);
// 0x2A27 Hardware Revision String
int device_hardware_revision_read(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt,
void *arg);
// 0x2A29 Manufacturer Name String
int device_manufacturer_read(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg); int device_manufacturer_read(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);

View File

@@ -10,3 +10,5 @@ int led_capabilities_read(uint16_t conn_handle, uint16_t attr_handle, struct ble
/// LED Service Characteristic User Description /// LED Service Characteristic User Description
int led_char_a000_user_desc(uint16_t con_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg); int led_char_a000_user_desc(uint16_t con_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);
int led_char_dead_user_desc(uint16_t con_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg); int led_char_dead_user_desc(uint16_t con_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);
int led_char_dead_presentation(uint16_t con_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);
int led_char_dead_valid_range(uint16_t con_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);

View File

@@ -1,6 +1,6 @@
#include "include/led_service.h" #include "include/light_service.h"
static const char *TAG = "led_service"; static const char *TAG = "light_service";
/// Capabilities of Device /// Capabilities of Device
int led_capabilities_read(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) int led_capabilities_read(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
@@ -73,3 +73,39 @@ int led_char_dead_user_desc(uint16_t conn_handle, uint16_t attr_handle, struct b
os_mbuf_append(ctxt->om, desc, strlen(desc)); os_mbuf_append(ctxt->om, desc, strlen(desc));
return 0; return 0;
} }
int led_char_dead_presentation(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
{
if (ctxt->op != BLE_GATT_ACCESS_OP_READ_DSC)
{
return BLE_ATT_ERR_READ_NOT_PERMITTED;
}
// GATT Presentation Format (0x2904), 7 Bytes:
// [format, exponent, unit(2), namespace, description(2)]
// format 0x04 = uint8, exponent 0, unit 0x2700 (unitless), namespace 1 (Bluetooth SIG Assigned Numbers),
// description 0
const uint8_t fmt[7] = {
0x04, // format = uint8
0x00, // exponent
0x00, 0x27, // unit = org.bluetooth.unit.unitless (0x2700)
0x01, // namespace = 1 (SIG)
0x00, 0x00 // description
};
return os_mbuf_append(ctxt->om, fmt, sizeof(fmt)) == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
int led_char_dead_valid_range(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
{
if (ctxt->op != BLE_GATT_ACCESS_OP_READ_DSC)
{
return BLE_ATT_ERR_READ_NOT_PERMITTED;
}
// Valid Range Descriptor (0x2906) Payload:
// - Für numerische Werte: [min .. max] im "nativen" Datentyp der Characteristic.
// Hier: uint8, also 1 Byte min + 1 Byte max.
const uint8_t range[2] = {0x00, 0x01}; // min=0, max=1
return os_mbuf_append(ctxt->om, range, sizeof(range)) == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}

View File

@@ -1,7 +1,6 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "capability_service.h"
#include "esp_event.h" #include "esp_event.h"
#include "esp_log.h" #include "esp_log.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
@@ -11,7 +10,7 @@
#include "host/ble_sm.h" #include "host/ble_sm.h"
#include "host/ble_uuid.h" #include "host/ble_uuid.h"
#include "include/device_service.h" #include "include/device_service.h"
#include "include/led_service.h" #include "include/light_service.h"
#include "nimble/nimble_port.h" #include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h" #include "nimble/nimble_port_freertos.h"
#include "sdkconfig.h" #include "sdkconfig.h"
@@ -20,10 +19,11 @@
static const char *TAG = "remote_control"; static const char *TAG = "remote_control";
// static const ble_uuid128_t capability_service_uuid =
// BLE_UUID128_INIT(0x91, 0xB6, 0xCA, 0x95, 0xB2, 0xC6, 0x7B, 0x90, 0x31, 0x45, 0x77, 0xE6, 0x67, 0x10, 0x68, 0xB9);
static const ble_uuid16_t device_service_uuid = BLE_UUID16_INIT(0x180A); static const ble_uuid16_t device_service_uuid = BLE_UUID16_INIT(0x180A);
static const ble_uuid128_t capability_service_uuid = static const ble_uuid16_t light_service_uuid = BLE_UUID16_INIT(0xA000);
BLE_UUID128_INIT(0x91, 0xB6, 0xCA, 0x95, 0xB2, 0xC6, 0x7B, 0x90, 0x31, 0x45, 0x77, 0xE6, 0x67, 0x10, 0x68, 0xB9);
static const ble_uuid16_t led_service_uuid = BLE_UUID16_INIT(0x1007);
uint8_t ble_addr_type; uint8_t ble_addr_type;
@@ -39,57 +39,63 @@ static struct ble_gatt_dsc_def char_0xA000_descs[] = {{
}, },
{0}}; {0}};
static struct ble_gatt_dsc_def char_0xDEAD_descs[] = {{ static struct ble_gatt_dsc_def char_0xDEAD_descs[] = {
.uuid = BLE_UUID16_DECLARE(0x2901), {
.att_flags = BLE_ATT_F_WRITE, .uuid = BLE_UUID16_DECLARE(0x2901),
.access_cb = led_char_dead_user_desc, .att_flags = BLE_ATT_F_WRITE,
}, .access_cb = led_char_dead_user_desc,
{0}}; },
{
static struct ble_gatt_dsc_def char_0x1979_desc[] = {{ .uuid = BLE_UUID16_DECLARE(0x2904), // Presentation Format (optional, empfehlenswert)
.uuid = BLE_UUID16_DECLARE(0x2901), .att_flags = BLE_ATT_F_READ,
.att_flags = BLE_ATT_F_READ, .access_cb = led_char_dead_presentation,
.access_cb = capa_char_1979_user_desc, },
}, {
{0}}; .uuid = BLE_UUID16_DECLARE(0x2906), // Valid Range
.att_flags = BLE_ATT_F_READ,
.access_cb = led_char_dead_valid_range,
},
{0}};
// Array of pointers to other service definitions // Array of pointers to other service definitions
static const struct ble_gatt_svc_def gatt_svcs[] = { static const struct ble_gatt_svc_def gatt_svcs[] = {
{ {
.type = BLE_GATT_SVC_TYPE_PRIMARY, .type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = &device_service_uuid.u, .uuid = &device_service_uuid.u,
.characteristics = (struct ble_gatt_chr_def[]){{.uuid = BLE_UUID16_DECLARE(0x2A24),
.flags = BLE_GATT_CHR_F_READ,
.access_cb = device_model_number_read},
{.uuid = BLE_UUID16_DECLARE(0x2A29),
.flags = BLE_GATT_CHR_F_READ,
.access_cb = device_manufacturer_read},
{0}},
},
{
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = &capability_service_uuid.u,
.characteristics = (struct ble_gatt_chr_def[]){{ .characteristics = (struct ble_gatt_chr_def[]){{
.uuid = BLE_UUID16_DECLARE(0x1979), .uuid = BLE_UUID16_DECLARE(0x2A29),
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY, .flags = BLE_GATT_CHR_F_READ,
.access_cb = capa_read, .access_cb = device_manufacturer_read,
.val_handle = &g_capa_char_val_handle, },
.descriptors = char_0x1979_desc, {
.uuid = BLE_UUID16_DECLARE(0x2A27),
.flags = BLE_GATT_CHR_F_READ,
.access_cb = device_hardware_revision_read,
},
{
.uuid = BLE_UUID16_DECLARE(0x2A26),
.flags = BLE_GATT_CHR_F_READ,
.access_cb = device_firmware_revision_read,
},
{
.uuid = BLE_UUID16_DECLARE(0x2A00),
.flags = BLE_GATT_CHR_F_READ,
.access_cb = device_name_read,
}, },
{0}}, {0}},
}, },
{ {
.type = BLE_GATT_SVC_TYPE_PRIMARY, .type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = &led_service_uuid.u, .uuid = &light_service_uuid.u,
.characteristics = (struct ble_gatt_chr_def[]){{ .characteristics = (struct ble_gatt_chr_def[]){{
.uuid = BLE_UUID16_DECLARE(0xA000), .uuid = BLE_UUID16_DECLARE(0xA000),
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY, .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE,
.access_cb = led_capabilities_read, .access_cb = led_capabilities_read,
.descriptors = char_0xA000_descs, .descriptors = char_0xA000_descs,
}, },
{ {
.uuid = BLE_UUID16_DECLARE(0xDEAD), .uuid = BLE_UUID16_DECLARE(0xDEAD),
.flags = BLE_GATT_CHR_F_WRITE, .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE,
.access_cb = led_write, .access_cb = led_write,
.descriptors = char_0xDEAD_descs, .descriptors = char_0xDEAD_descs,
}, },
@@ -140,7 +146,7 @@ static int ble_gap_event(struct ble_gap_event *event, void *arg)
{ {
ESP_LOGI(TAG, "Client subscribed to capability notifications. Sending data..."); ESP_LOGI(TAG, "Client subscribed to capability notifications. Sending data...");
// Call the function to send capability data via notifications // Call the function to send capability data via notifications
capa_notify_data(event->subscribe.conn_handle, g_capa_char_val_handle); // capa_notify_data(event->subscribe.conn_handle, g_capa_char_val_handle);
} }
else else
{ {
@@ -163,10 +169,15 @@ static void ble_app_advertise(void)
// GAP - advertising definition // GAP - advertising definition
struct ble_hs_adv_fields fields; struct ble_hs_adv_fields fields;
memset(&fields, 0, sizeof(fields)); memset(&fields, 0, sizeof(fields));
uint8_t mfg_data[] = {0xDE, 0xC0, 0x05, 0x10, 0x20, 0x25};
static const ble_uuid16_t services[] = {device_service_uuid, light_service_uuid};
fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP; fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP;
fields.uuids128 = (ble_uuid128_t[]){capability_service_uuid}; fields.uuids16 = services;
fields.num_uuids128 = 1; fields.num_uuids16 = sizeof(services) / sizeof(services[0]);
fields.uuids128_is_complete = 1; fields.uuids16_is_complete = 1;
fields.mfg_data = mfg_data;
fields.mfg_data_len = sizeof(mfg_data);
ret = ble_gap_adv_set_fields(&fields); ret = ble_gap_adv_set_fields(&fields);
if (ret != 0) if (ret != 0)

View File

@@ -1,6 +0,0 @@
idf_component_register(SRCS
"storage.c"
INCLUDE_DIRS "include"
PRIV_REQUIRES
spiffs
)

View File

@@ -1,55 +0,0 @@
#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);

View File

@@ -1,139 +0,0 @@
#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 Storage");
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;
}

View File

@@ -1,47 +0,0 @@
{
"module": "Lighthouse",
"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"
}
]
}

View File

@@ -2,4 +2,3 @@ idf_component_register(SRCS
"main.c" "main.c"
INCLUDE_DIRS "." INCLUDE_DIRS "."
) )
spiffs_create_partition_image(storage ../data FLASH_IN_PROJECT)

View File

@@ -1,7 +1,6 @@
# Name , Type , SubType , Offset , Size , Flags # Name , Type , SubType , Offset , Size , Flags
nvs , data , nvs , 0x9000 , 20k , nvs , data , nvs , 0x9000 , 20k ,
otadata , data , ota , 0xe000 , 8k , otadata , data , ota , 0xe000 , 8k ,
app0 , app , ota_0 , 0x10000 , 1024k , app0 , app , ota_0 , 0x10000 , 1792k ,
app1 , app , ota_1 , , 1024k , app1 , app , ota_1 , , 1792k ,
storage , data , spiffs , , 1536k ,
coredump , data , coredump , , 64k , coredump , data , coredump , , 64k ,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 20k
3 otadata data ota 0xe000 8k
4 app0 app ota_0 0x10000 1024k 1792k
5 app1 app ota_1 1024k 1792k
storage data spiffs 1536k
6 coredump data coredump 64k

View File

@@ -6,11 +6,6 @@ CONFIG_BT_NIMBLE_ENABLED=y
CONFIG_BT_NIMBLE_SECURITY_ENABLE=n CONFIG_BT_NIMBLE_SECURITY_ENABLE=n
CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME="lighthouse" CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME="lighthouse"
# Logging
CONFIG_LOG_DEFAULT_LEVEL_INFO=y
CONFIG_LOG_DEFAULT_LEVEL=3
CONFIG_LOG_MAXIMUM_LEVEL=3
# Flash Size # Flash Size
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_ESPTOOLPY_FLASHSIZE="4MB" CONFIG_ESPTOOLPY_FLASHSIZE="4MB"

View File

@@ -0,0 +1,12 @@
# Core dump
CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH=y
CONFIG_ESP_COREDUMP_CAPTURE_DRAM=y
# Build type
CONFIG_APP_REPRODUCIBLE_BUILD=y
# LOG
CONFIG_BOOTLOADER_LOG_LEVEL_NONE=y
# Log Level
CONFIG_LOG_DEFAULT_LEVEL_NONE=Y