Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
version: 2

updates:
- package-ecosystem: gitsubmodule
schedule:
interval: "daily"
directory: "."
4 changes: 2 additions & 2 deletions .github/workflows/validate-component.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
python-version: '3.12'

- name: Install IDF Component Manager
run: pip install 'idf-component-manager==1.5.2'
run: pip install idf-component-manager

- name: Validate and Pack Component
run: compote component pack --name Notecard
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,11 @@ Component config ---> Notecard Configuration
## Thread Safety

The component automatically provides thread-safe access to the Notecard in multi-threaded FreeRTOS applications.
This is specific to the ESP-IDF implementation of `malloc` as referenced in their [documentation](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/mem_alloc.html#thread-safety).
The underlying [note-c](https://github.com/blues/note-c) library protects the Notecard from concurrent access using internal mutexes, so no additional locking is required for normal use.

> Note: Due to the nature of note-c and how it protects the Notecard from concurrent access, if you application has sensitive timing requirements, your Notecard operations may need to be handled in their own task. Consider using a message queue to offload data to a Notecard send/receive task.

### I2C Bus Sharing

If you have other I2C peripherals on the same bus as the Notecard, register your I2C mutex with `note-c` using `NoteSetFnI2CMutex()` to minimize the time spent under lock.
Expand Down
5 changes: 3 additions & 2 deletions examples/basic_usage_i2c/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ This example demonstrates basic Notecard usage with ESP-IDF in a standard ESP-ID
- Initialize the Notecard with I2C communication using the new I2C master API
- Configure Notehub connection
- Use FreeRTOS tasks for sensor data collection
- Read built-in sensors from the Notecard (temperature and voltage)
- Read built-in sensors from the Notecard (temperature)
- Send sensor data to Notehub using a dedicated task
- Handle JSON requests and responses with proper thread safety
- Handle JSON requests and responses
- Properly deinitialize the Notecard on cleanup

## Hardware Requirements

Expand Down
110 changes: 33 additions & 77 deletions examples/basic_usage_i2c/main/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,18 @@
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_chip_info.h"
#include "esp_timer.h"
#include "driver/i2c_master.h"
#include "notecard.h"

// Platform function declaration for I2C scan
extern esp_err_t notecard_platform_i2c_scan(uint8_t start_addr, uint8_t end_addr, uint8_t *found_devices, uint8_t max_devices, uint8_t *device_count);

static const char *TAG = "notecard_basic";

// Task handles
static TaskHandle_t notecard_task_handle = NULL;
static bool notecard_initialized = false;
static SemaphoreHandle_t notecard_mutex = NULL;

// Task stack sizes
#define NOTECARD_TASK_STACK_SIZE (4 * 1024)
Expand Down Expand Up @@ -74,10 +69,6 @@ static esp_err_t notecard_configure_hub(void)

// Create hub.set request
J *req = NoteNewRequest("hub.set");
if (req == NULL) {
ESP_LOGE(TAG, "Failed to create hub.set request");
return ESP_ERR_NO_MEM;
}

// Set Notehub product ID
JAddStringToObject(req, "product", CONFIG_NOTEHUB_PRODUCT_UID);
Expand Down Expand Up @@ -108,54 +99,34 @@ static void notecard_sensor_task(void *pvParameters)
const TickType_t reading_interval = pdMS_TO_TICKS(15000); // 15 seconds

while (eventCounter < max_readings) {
if (xSemaphoreTake(notecard_mutex, portMAX_DELAY) == pdTRUE) {
eventCounter++;

ESP_LOGI(TAG, "Reading sensors (sample %d/%d)...", eventCounter, max_readings);

// Read temperature from Notecard's built-in sensor
double temperature = 0;
J *rsp = NoteRequestResponse(NoteNewRequest("card.temp"));
if (rsp != NULL) {
temperature = JGetNumber(rsp, "value");
NoteDeleteResponse(rsp);
} else {
ESP_LOGW(TAG, "Failed to read temperature");
}

// Read voltage from Notecard's V+ pin
double voltage = 0;
rsp = NoteRequestResponse(NoteNewRequest("card.voltage"));
if (rsp != NULL) {
voltage = JGetNumber(rsp, "value");
NoteDeleteResponse(rsp);
} else {
ESP_LOGW(TAG, "Failed to read voltage");
}

// Send data to Notehub
J *req = NoteNewRequest("note.add");
if (req != NULL) {
JAddBoolToObject(req, "sync", true);
J *body = JAddObjectToObject(req, "body");
if (body != NULL) {
JAddNumberToObject(body, "temp", temperature);
JAddNumberToObject(body, "voltage", voltage);
JAddNumberToObject(body, "count", eventCounter);
}

bool success = NoteRequest(req);
if (success) {
ESP_LOGI(TAG, "Sample %d sent: temp=%.2f°C, voltage=%.2fV",
eventCounter, temperature, voltage);
} else {
ESP_LOGE(TAG, "Failed to send sample %d", eventCounter);
}
} else {
ESP_LOGE(TAG, "Failed to create note request");
}

xSemaphoreGive(notecard_mutex);
eventCounter++;

ESP_LOGI(TAG, "Reading sensors (sample %d/%d)...", eventCounter, max_readings);

// Read temperature from Notecard's built-in sensor
double temperature = 0;
J *rsp = NoteRequestResponse(NoteNewRequest("card.temp"));
if (rsp != NULL) {
temperature = JGetNumber(rsp, "value");
NoteDeleteResponse(rsp);
} else {
ESP_LOGW(TAG, "Failed to read temperature");
}

// Send data to Notehub
J *req = NoteNewRequest("note.add");
JAddBoolToObject(req, "sync", true);

J *body = JAddObjectToObject(req, "body");
JAddNumberToObject(body, "temp", temperature);
JAddNumberToObject(body, "count", eventCounter);

bool success = NoteRequest(req);
if (success) {
ESP_LOGI(TAG, "Sample %d sent: temp=%.2f°C",
eventCounter, temperature);
} else {
ESP_LOGE(TAG, "Failed to send sample %d", eventCounter);
}

// Wait before next reading
Expand All @@ -181,27 +152,14 @@ void app_main(void)
CONFIG_IDF_TARGET, chip_info.revision, chip_info.cores);
ESP_LOGI(TAG, "Free heap: %u bytes", esp_get_free_heap_size());

// Create mutex for Notecard access
notecard_mutex = xSemaphoreCreateMutex();
if (notecard_mutex == NULL) {
ESP_LOGE(TAG, "Failed to create Notecard mutex");
return;
}

// Initialize Notecard hardware and configure for Notehub
// Use mutex to protect note-c global state during initialization
esp_err_t ret = ESP_FAIL;
if (xSemaphoreTake(notecard_mutex, portMAX_DELAY) == pdTRUE) {
ret = notecard_hardware_init();
if (ret == ESP_OK) {
ret = notecard_configure_hub();
}
xSemaphoreGive(notecard_mutex);
esp_err_t ret = notecard_hardware_init();
if (ret == ESP_OK) {
ret = notecard_configure_hub();
}

if (ret != ESP_OK) {
ESP_LOGE(TAG, "Notecard initialization failed");
vSemaphoreDelete(notecard_mutex);
return;
}

Expand Down Expand Up @@ -240,10 +198,8 @@ void app_main(void)
(notecard_task_handle != NULL) ? "yes" : "no");
}

// Cleanup
if (notecard_mutex) {
vSemaphoreDelete(notecard_mutex);
}
// Cleanup - deinitialize Notecard
notecard_deinit();

ESP_LOGI(TAG, "Application ended gracefully");
}
5 changes: 3 additions & 2 deletions examples/basic_usage_uart/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ This example demonstrates basic Notecard usage with ESP-IDF using UART communica
- Initialize the Notecard with UART communication
- Configure Notehub connection
- Use FreeRTOS tasks for sensor data collection
- Read built-in sensors from the Notecard (temperature and voltage)
- Read built-in sensors from the Notecard (temperature)
- Send sensor data to Notehub using a dedicated task
- Handle JSON requests and responses with proper thread safety
- Handle JSON requests and responses
- Properly deinitialize the Notecard on cleanup

## Hardware Requirements

Expand Down
107 changes: 33 additions & 74 deletions examples/basic_usage_uart/main/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_chip_info.h"
Expand All @@ -44,7 +43,6 @@ static const char *TAG = "notecard_uart";
// Task handles
static TaskHandle_t notecard_task_handle = NULL;
static bool notecard_initialized = false;
static SemaphoreHandle_t notecard_mutex = NULL;

// Task stack sizes
#define NOTECARD_TASK_STACK_SIZE (4 * 1024)
Expand Down Expand Up @@ -82,10 +80,6 @@ static esp_err_t notecard_configure_hub(void)

// Create hub.set request
J *req = NoteNewRequest("hub.set");
if (req == NULL) {
ESP_LOGE(TAG, "Failed to create hub.set request");
return ESP_ERR_NO_MEM;
}

// Set Notehub product ID
JAddStringToObject(req, "product", CONFIG_NOTEHUB_PRODUCT_UID);
Expand Down Expand Up @@ -116,54 +110,34 @@ static void notecard_sensor_task(void *pvParameters)
const TickType_t reading_interval = pdMS_TO_TICKS(15000); // 15 seconds

while (eventCounter < max_readings) {
if (xSemaphoreTake(notecard_mutex, portMAX_DELAY) == pdTRUE) {
eventCounter++;

ESP_LOGI(TAG, "Reading sensors (sample %d/%d)...", eventCounter, max_readings);

// Read temperature from Notecard's built-in sensor
double temperature = 0;
J *rsp = NoteRequestResponse(NoteNewRequest("card.temp"));
if (rsp != NULL) {
temperature = JGetNumber(rsp, "value");
NoteDeleteResponse(rsp);
} else {
ESP_LOGW(TAG, "Failed to read temperature");
}

// Read voltage from Notecard's V+ pin
double voltage = 0;
rsp = NoteRequestResponse(NoteNewRequest("card.voltage"));
if (rsp != NULL) {
voltage = JGetNumber(rsp, "value");
NoteDeleteResponse(rsp);
} else {
ESP_LOGW(TAG, "Failed to read voltage");
}

// Send data to Notehub
J *req = NoteNewRequest("note.add");
if (req != NULL) {
JAddBoolToObject(req, "sync", true);
J *body = JAddObjectToObject(req, "body");
if (body != NULL) {
JAddNumberToObject(body, "temp", temperature);
JAddNumberToObject(body, "voltage", voltage);
JAddNumberToObject(body, "count", eventCounter);
}

bool success = NoteRequest(req);
if (success) {
ESP_LOGI(TAG, "Sample %d sent: temp=%.2f°C, voltage=%.2fV",
eventCounter, temperature, voltage);
} else {
ESP_LOGE(TAG, "Failed to send sample %d", eventCounter);
}
} else {
ESP_LOGE(TAG, "Failed to create note request");
}

xSemaphoreGive(notecard_mutex);
eventCounter++;

ESP_LOGI(TAG, "Reading sensors (sample %d/%d)...", eventCounter, max_readings);

// Read temperature from Notecard's built-in sensor
double temperature = 0;
J *rsp = NoteRequestResponse(NoteNewRequest("card.temp"));
if (rsp != NULL) {
temperature = JGetNumber(rsp, "value");
NoteDeleteResponse(rsp);
} else {
ESP_LOGW(TAG, "Failed to read temperature");
}

// Send data to Notehub
J *req = NoteNewRequest("note.add");
JAddBoolToObject(req, "sync", true);

J *body = JAddObjectToObject(req, "body");
JAddNumberToObject(body, "temp", temperature);
JAddNumberToObject(body, "count", eventCounter);

bool success = NoteRequest(req);
if (success) {
ESP_LOGI(TAG, "Sample %d sent: temp=%.2f°C",
eventCounter, temperature);
} else {
ESP_LOGE(TAG, "Failed to send sample %d", eventCounter);
}

// Wait before next reading
Expand All @@ -189,27 +163,14 @@ void app_main(void)
CONFIG_IDF_TARGET, chip_info.revision, chip_info.cores);
ESP_LOGI(TAG, "Free heap: %u bytes", esp_get_free_heap_size());

// Create mutex for Notecard access
notecard_mutex = xSemaphoreCreateMutex();
if (notecard_mutex == NULL) {
ESP_LOGE(TAG, "Failed to create Notecard mutex");
return;
}

// Initialize Notecard hardware and configure for Notehub
// Use mutex to protect note-c global state during initialization
esp_err_t ret = ESP_FAIL;
if (xSemaphoreTake(notecard_mutex, portMAX_DELAY) == pdTRUE) {
ret = notecard_hardware_init();
if (ret == ESP_OK) {
ret = notecard_configure_hub();
}
xSemaphoreGive(notecard_mutex);
esp_err_t ret = notecard_hardware_init();
if (ret == ESP_OK) {
ret = notecard_configure_hub();
}

if (ret != ESP_OK) {
ESP_LOGE(TAG, "Notecard initialization failed");
vSemaphoreDelete(notecard_mutex);
return;
}

Expand Down Expand Up @@ -248,10 +209,8 @@ void app_main(void)
(notecard_task_handle != NULL) ? "yes" : "no");
}

// Cleanup
if (notecard_mutex) {
vSemaphoreDelete(notecard_mutex);
}
// Cleanup - deinitialize Notecard
notecard_deinit();

ESP_LOGI(TAG, "Application ended gracefully");
}
Loading