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
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.x'
python-version: '3.11'

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

- name: Validate and Pack Component
run: compote component pack --name Notecard
8 changes: 8 additions & 0 deletions Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ menu "Notecard Configuration"
help
Enable internal pull-up resistors for I2C lines.
Disable this if you have external pull-up resistors.

config NOTECARD_I2C_MUTEX
bool "Enable I2C mutex"
default y
help
Enable I2C mutex callback to prevent concurrent access to the I2C bus.
Disable if you intend to define your own I2C bus mutex.

endmenu

menu "Default UART Configuration"
Expand Down
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# ESP-IDF Notecard Component

An ESP-IDF component for integrating Espressif devices with the Blues [Notecard](https://blues.com/products/notecard/).
This component provides a thread-safe interface to the Notecard using the [note-c](https://github.com/blues/note-c) library.

## Installation

Expand Down Expand Up @@ -44,6 +45,36 @@ idf.py menuconfig
Component config ---> Notecard Configuration
```

## Thread Safety

The component automatically provides thread-safe access to the Notecard in multi-threaded FreeRTOS applications.
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.

### 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.

For your convenience, we have provided a default implementation of I2C mutex APIs to coordinate access (example shown below):

```c
#include "notecard.h"

// Access your I2C peripherals
notecard_i2c_lock();
i2c_master_transmit(my_peripheral_handle, data, len, timeout);
notecard_i2c_unlock();
```

This ensures the `note-c` won't attempt I2C communication while you're accessing your other peripherals.

In order to enable/disable the provided I2C bus mutex (e.g. when using your own mutex), use `menuconfig`:

```
Component config ---> Notecard Configuration ---> Default I2C Configuration ---> [ ] Enable I2C mutex
```

> Note: The I2C mutex is enabled by default.

## Examples

For examples, see the [examples](examples) directory.
Expand Down
2 changes: 1 addition & 1 deletion examples/basic_usage_i2c/dependencies.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ dependencies:
version: 5.5.1
direct_dependencies:
- idf
manifest_hash: 08ac1ee392ce13ac722569147b417c0484712da30ecb148c8d70f9d19fd69d84
manifest_hash: cbc3626899740395aaaf5addbe70903d6950040e23cbea45719d4e50ea14d749
target: esp32
version: 2.0.0
2 changes: 1 addition & 1 deletion examples/basic_usage_uart/dependencies.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ dependencies:
version: 5.5.1
direct_dependencies:
- idf
manifest_hash: 08ac1ee392ce13ac722569147b417c0484712da30ecb148c8d70f9d19fd69d84
manifest_hash: cbc3626899740395aaaf5addbe70903d6950040e23cbea45719d4e50ea14d749
target: esp32
version: 2.0.0
18 changes: 13 additions & 5 deletions idf_component.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,16 @@ maintainers:
- "Support <support@blues.com>"

files:
use_gitignore: true

examples:
- path: examples/basic_usage_i2c
- path: examples/basic_usage_uart
exclude:
- "build/**/*"
- "dist/**/*"
- ".vscode/**/*"
- ".idea/**/*"
- "*.swp"
- "*.swo"
- ".DS_Store"
- "Thumbs.db"
- "sdkconfig"
- "sdkconfig.old"
- "CMakeCache.txt"
- "CMakeFiles/**/*"
29 changes: 29 additions & 0 deletions include/notecard.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,35 @@ bool notecard_is_initialized(void);
*/
void notecard_set_logging(bool enable);

#ifdef CONFIG_NOTECARD_I2C_MUTEX
/**
* @brief Lock the I2C bus mutex
*
* Use this when accessing other I2C peripherals on the same bus to prevent
* conflicts with Notecard I2C operations. Always pair with notecard_i2c_unlock().
*
* @note Only available if CONFIG_NOTECARD_I2C_MUTEX is enabled in Kconfig
*
* Example:
* @code
* notecard_i2c_lock();
* // Access your I2C sensor here
* i2c_master_transmit(my_sensor_handle, data, len, timeout);
* notecard_i2c_unlock();
* @endcode
*/
void notecard_i2c_lock(void);

/**
* @brief Unlock the I2C bus mutex
*
* Must be called after notecard_i2c_lock() to release the I2C bus.
*
* @note Only available if CONFIG_NOTECARD_I2C_MUTEX is enabled in Kconfig
*/
void notecard_i2c_unlock(void);
#endif

#ifdef __cplusplus
}
#endif
Expand Down
3 changes: 3 additions & 0 deletions src/notecard.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ esp_err_t notecard_init(const notecard_config_t *config)
notecard_platform_delay,
notecard_platform_millis);

// Register mutex hooks for thread safety
notecard_platform_register_mutex_hooks();

// Set up logging output
if (g_logging_enabled) {
NoteSetFnDebugOutput(notecard_logging_output);
Expand Down
134 changes: 134 additions & 0 deletions src/notecard_platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "driver/i2c_master.h"
#include "driver/uart.h"
#include <string.h>
Expand All @@ -22,6 +23,46 @@ static bool g_i2c_initialized = false;
static bool g_uart_initialized = false;
static i2c_master_bus_handle_t g_i2c_bus_handle = NULL;

// FreeRTOS mutex handles for thread-safe access
#ifdef CONFIG_NOTECARD_I2C_MUTEX
static SemaphoreHandle_t g_i2c_mutex = NULL;
#endif
static SemaphoreHandle_t g_notecard_mutex = NULL;

//=============================================================================
// Mutex Platform Implementation
//=============================================================================

#ifdef CONFIG_NOTECARD_I2C_MUTEX
static void notecard_platform_i2c_lock(void)
{
if (g_i2c_mutex != NULL) {
for (;xSemaphoreTake(g_i2c_mutex, portMAX_DELAY) != pdTRUE;);
}
}

static void notecard_platform_i2c_unlock(void)
{
if (g_i2c_mutex != NULL) {
xSemaphoreGive(g_i2c_mutex);
}
}
#endif

static void notecard_platform_note_lock(void)
{
if (g_notecard_mutex != NULL) {
for (;xSemaphoreTake(g_notecard_mutex, portMAX_DELAY) != pdTRUE;);
}
}

static void notecard_platform_note_unlock(void)
{
if (g_notecard_mutex != NULL) {
xSemaphoreGive(g_notecard_mutex);
}
}

//=============================================================================
// I2C Platform Implementation
//=============================================================================
Expand All @@ -37,6 +78,27 @@ esp_err_t notecard_platform_i2c_init(const notecard_i2c_config_t *config)
return ESP_ERR_INVALID_ARG;
}

// Create mutexes
if (g_notecard_mutex == NULL) {
g_notecard_mutex = xSemaphoreCreateMutex();
if (g_notecard_mutex == NULL) {
ESP_LOGE(TAG, "Failed to create Notecard mutex");
return ESP_ERR_NO_MEM;
}
}

#ifdef CONFIG_NOTECARD_I2C_MUTEX
if (g_i2c_mutex == NULL) {
g_i2c_mutex = xSemaphoreCreateMutex();
if (g_i2c_mutex == NULL) {
ESP_LOGE(TAG, "Failed to create I2C mutex");
vSemaphoreDelete(g_notecard_mutex);
g_notecard_mutex = NULL;
return ESP_ERR_NO_MEM;
}
}
#endif

// Store configuration
memcpy(&g_i2c_config, config, sizeof(notecard_i2c_config_t));

Expand All @@ -55,6 +117,12 @@ esp_err_t notecard_platform_i2c_init(const notecard_i2c_config_t *config)
esp_err_t ret = i2c_new_master_bus(&bus_config, &g_i2c_bus_handle);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "I2C master bus creation failed: %s", esp_err_to_name(ret));
#ifdef CONFIG_NOTECARD_I2C_MUTEX
vSemaphoreDelete(g_i2c_mutex);
g_i2c_mutex = NULL;
#endif
vSemaphoreDelete(g_notecard_mutex);
g_notecard_mutex = NULL;
return ret;
}

Expand All @@ -79,6 +147,20 @@ esp_err_t notecard_platform_i2c_deinit(void)

g_i2c_bus_handle = NULL;
g_i2c_initialized = false;

// Clean up mutexes
#ifdef CONFIG_NOTECARD_I2C_MUTEX
if (g_i2c_mutex != NULL) {
vSemaphoreDelete(g_i2c_mutex);
g_i2c_mutex = NULL;
}
#endif

if (g_notecard_mutex != NULL) {
vSemaphoreDelete(g_notecard_mutex);
g_notecard_mutex = NULL;
}

ESP_LOGI(TAG, "I2C deinitialized");
return ESP_OK;
}
Expand Down Expand Up @@ -228,6 +310,15 @@ esp_err_t notecard_platform_uart_init(const notecard_uart_config_t *config)
return ESP_ERR_INVALID_ARG;
}

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

memcpy(&g_uart_config, config, sizeof(notecard_uart_config_t));

uart_config_t uart_conf = {
Expand Down Expand Up @@ -257,6 +348,8 @@ esp_err_t notecard_platform_uart_init(const notecard_uart_config_t *config)
config->tx_buffer_size, 0, NULL, 0);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "UART driver install failed: %s", esp_err_to_name(ret));
vSemaphoreDelete(g_notecard_mutex);
g_notecard_mutex = NULL;
return ret;
}

Expand All @@ -280,6 +373,13 @@ esp_err_t notecard_platform_uart_deinit(void)
}

g_uart_initialized = false;

// Clean up Notecard mutex
if (g_notecard_mutex != NULL) {
vSemaphoreDelete(g_notecard_mutex);
g_notecard_mutex = NULL;
}

ESP_LOGI(TAG, "UART deinitialized");
return ESP_OK;
}
Expand Down Expand Up @@ -385,3 +485,37 @@ void notecard_platform_free(void *ptr)
{
free(ptr);
}

//=============================================================================
// Mutex Hook Registration
//=============================================================================

void notecard_platform_register_mutex_hooks(void)
{
// Register mutex hooks with note-c based on Kconfig settings
// Enable Notecard mutexes (I2C mutex is optional)
NoteSetFnNoteMutex(notecard_platform_note_lock,
notecard_platform_note_unlock);

#ifdef CONFIG_NOTECARD_I2C_MUTEX
// Enable I2C mutexes
NoteSetFnI2CMutex(notecard_platform_i2c_lock,
notecard_platform_i2c_unlock);
#endif
}

//=============================================================================
// Public I2C Mutex API
//=============================================================================

#ifdef CONFIG_NOTECARD_I2C_MUTEX
void notecard_i2c_lock(void)
{
notecard_platform_i2c_lock();
}

void notecard_i2c_unlock(void)
{
notecard_platform_i2c_unlock();
}
#endif
9 changes: 9 additions & 0 deletions src/notecard_platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ void notecard_platform_delay(uint32_t ms);
void *notecard_platform_malloc(size_t size);
void notecard_platform_free(void *ptr);

// Mutex registration function
void notecard_platform_register_mutex_hooks(void);

#ifdef CONFIG_NOTECARD_I2C_MUTEX
// Public I2C mutex functions
void notecard_i2c_lock(void);
void notecard_i2c_unlock(void);
#endif

#ifdef __cplusplus
}
#endif
Expand Down