Files
BotanicalBuddy/components/i2c_master_messager/i2c_master_messager.c

190 lines
4.8 KiB
C

#include <string.h>
#include "driver/i2c_master.h"
#include "esp_check.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "i2c_master_messager.h"
static const char *TAG = "i2c_master_messager";
typedef struct {
bool initialized;
i2c_master_messager_config_t config;
i2c_master_bus_handle_t i2c_bus;
bh1750_handle_t bh1750;
i2c_master_messager_data_t data;
SemaphoreHandle_t lock;
TaskHandle_t task_handle;
} i2c_master_messager_ctx_t;
static i2c_master_messager_ctx_t g_ctx;
static void i2c_master_messager_task(void *arg)
{
(void)arg;
while (1) {
float lux = 0.0f;
esp_err_t ret = bh1750_get_data(g_ctx.bh1750, &lux);
if (xSemaphoreTake(g_ctx.lock, pdMS_TO_TICKS(100)) == pdTRUE) {
g_ctx.data.bh1750.valid = (ret == ESP_OK);
g_ctx.data.bh1750.last_error = ret;
if (ret == ESP_OK) {
g_ctx.data.bh1750.lux = lux;
g_ctx.data.bh1750.last_update_ms = esp_timer_get_time() / 1000;
}
xSemaphoreGive(g_ctx.lock);
}
if (ret != ESP_OK) {
ESP_LOGW(TAG, "bh1750_get_data failed: %s", esp_err_to_name(ret));
}
vTaskDelay(pdMS_TO_TICKS(g_ctx.config.read_period_ms));
}
}
esp_err_t i2c_master_messager_init(const i2c_master_messager_config_t *config)
{
ESP_RETURN_ON_FALSE(config != NULL, ESP_ERR_INVALID_ARG, TAG, "config is null");
ESP_RETURN_ON_FALSE(config->read_period_ms >= I2C_MASTER_MESSAGER_MIN_PERIOD_MS,
ESP_ERR_INVALID_ARG,
TAG,
"read_period_ms too small");
if (g_ctx.initialized) {
return ESP_ERR_INVALID_STATE;
}
memset(&g_ctx, 0, sizeof(g_ctx));
g_ctx.config = *config;
g_ctx.lock = xSemaphoreCreateMutex();
ESP_RETURN_ON_FALSE(g_ctx.lock != NULL, ESP_ERR_NO_MEM, TAG, "failed to create mutex");
const i2c_master_bus_config_t bus_cfg = {
.i2c_port = config->i2c_port,
.sda_io_num = config->sda_io_num,
.scl_io_num = config->scl_io_num,
.clk_source = I2C_CLK_SRC_DEFAULT,
.glitch_ignore_cnt = 7,
.flags.enable_internal_pullup = true,
};
esp_err_t ret = i2c_new_master_bus(&bus_cfg, &g_ctx.i2c_bus);
if (ret != ESP_OK) {
vSemaphoreDelete(g_ctx.lock);
g_ctx.lock = NULL;
return ret;
}
ret = bh1750_create(g_ctx.i2c_bus, config->bh1750_addr, &g_ctx.bh1750);
if (ret != ESP_OK) {
i2c_del_master_bus(g_ctx.i2c_bus);
g_ctx.i2c_bus = NULL;
vSemaphoreDelete(g_ctx.lock);
g_ctx.lock = NULL;
return ret;
}
ESP_GOTO_ON_ERROR(bh1750_power_on(g_ctx.bh1750), err, TAG, "bh1750_power_on failed");
ESP_GOTO_ON_ERROR(bh1750_set_measure_mode(g_ctx.bh1750, config->bh1750_mode),
err,
TAG,
"bh1750_set_measure_mode failed");
g_ctx.data.bh1750.valid = false;
g_ctx.data.bh1750.last_error = ESP_ERR_INVALID_STATE;
g_ctx.initialized = true;
ESP_LOGI(TAG,
"initialized: port=%d scl=%d sda=%d addr=0x%02X period=%u ms",
config->i2c_port,
config->scl_io_num,
config->sda_io_num,
config->bh1750_addr,
config->read_period_ms);
return ESP_OK;
err:
bh1750_delete(g_ctx.bh1750);
g_ctx.bh1750 = NULL;
i2c_del_master_bus(g_ctx.i2c_bus);
g_ctx.i2c_bus = NULL;
vSemaphoreDelete(g_ctx.lock);
g_ctx.lock = NULL;
return ret;
}
esp_err_t i2c_master_messager_start(void)
{
ESP_RETURN_ON_FALSE(g_ctx.initialized, ESP_ERR_INVALID_STATE, TAG, "not initialized");
if (g_ctx.task_handle != NULL) {
return ESP_OK;
}
BaseType_t ok = xTaskCreate(i2c_master_messager_task,
"i2c_msg_task",
4096,
NULL,
5,
&g_ctx.task_handle);
ESP_RETURN_ON_FALSE(ok == pdPASS, ESP_ERR_NO_MEM, TAG, "failed to create task");
return ESP_OK;
}
esp_err_t i2c_master_messager_stop(void)
{
ESP_RETURN_ON_FALSE(g_ctx.initialized, ESP_ERR_INVALID_STATE, TAG, "not initialized");
if (g_ctx.task_handle != NULL) {
vTaskDelete(g_ctx.task_handle);
g_ctx.task_handle = NULL;
}
return ESP_OK;
}
esp_err_t i2c_master_messager_get_data(i2c_master_messager_data_t *out_data)
{
ESP_RETURN_ON_FALSE(g_ctx.initialized, ESP_ERR_INVALID_STATE, TAG, "not initialized");
ESP_RETURN_ON_FALSE(out_data != NULL, ESP_ERR_INVALID_ARG, TAG, "out_data is null");
ESP_RETURN_ON_FALSE(g_ctx.lock != NULL, ESP_ERR_INVALID_STATE, TAG, "lock not ready");
ESP_RETURN_ON_FALSE(xSemaphoreTake(g_ctx.lock, pdMS_TO_TICKS(100)) == pdTRUE,
ESP_ERR_TIMEOUT,
TAG,
"failed to lock shared data");
*out_data = g_ctx.data;
xSemaphoreGive(g_ctx.lock);
return ESP_OK;
}
esp_err_t i2c_master_messager_deinit(void)
{
if (!g_ctx.initialized) {
return ESP_OK;
}
i2c_master_messager_stop();
if (g_ctx.bh1750 != NULL) {
bh1750_delete(g_ctx.bh1750);
g_ctx.bh1750 = NULL;
}
if (g_ctx.i2c_bus != NULL) {
i2c_del_master_bus(g_ctx.i2c_bus);
g_ctx.i2c_bus = NULL;
}
if (g_ctx.lock != NULL) {
vSemaphoreDelete(g_ctx.lock);
g_ctx.lock = NULL;
}
memset(&g_ctx, 0, sizeof(g_ctx));
return ESP_OK;
}