modbus
Ondřej Hruška 2 years ago
parent 8e9913dae5
commit 83d62c0ef7
  1. 12
      main/Kconfig.projbuild
  2. 149
      main/co2_sensor.c
  3. 164
      main/co2_sensor_i2c.c
  4. 57
      main/periph_init.c
  5. 11
      sdkconfig

@ -16,6 +16,18 @@ menu "App Configuration"
config PIN_I2C_SCL1
int "I2C0 SCL"
default 18
config PIN_CO2_NRDY
int "CO2 NRDY"
default 23
config PIN_CO2_COMSEL
int "CO2 COMSEL"
default 13
config PIN_CO2_EN
int "CO2 EN"
default 15
endmenu
menu "Console"

@ -13,141 +13,57 @@
#include "i2c_utils.h"
#include "periph_init.h"
#include "data_report.h"
#include <driver/uart.h>
static const char *TAG = "co2";
#define CO2_ADDR 104
#define CO2_I2C_NUM I2C_NUM_1
#define CO2_UART_NUM 1
#define TIMEOUT_MS 500
// this works, but the bus is fucked up
bool wake_up() {
esp_err_t suc;
#define TRY(x) suc=x; if(suc!=ESP_OK) return suc;
for (int i = 0; i < 5; i++) {
ESP_LOGD(TAG, "Wake up try %d", i);
uint8_t dummy;
i2c_cmd_handle_t chain;
chain = i2c_cmd_link_create();
TRY(i2c_master_start(chain));
TRY(i2c_master_write_byte(chain, (CO2_ADDR << 1) | I2C_MASTER_WRITE, false));
TRY(i2c_master_write_byte(chain, 0x30, false));
TRY(i2c_master_start(chain));
TRY(i2c_master_write_byte(chain, (CO2_ADDR << 1) | I2C_MASTER_READ, false)); // TODO expect ack?
TRY(i2c_master_read(chain, &dummy, 1, I2C_MASTER_LAST_NACK));
TRY(i2c_master_stop(chain));
i2c_master_cmd_begin(CO2_I2C_NUM, chain, pdMS_TO_TICKS(70));
i2c_cmd_link_delete(chain);
chain = i2c_cmd_link_create();
TRY(i2c_master_start(chain));
TRY(i2c_master_write_byte(chain, (CO2_ADDR << 1) | I2C_MASTER_WRITE, false));
TRY(i2c_master_write_byte(chain, 0x30, false));
TRY(i2c_master_start(chain));
TRY(i2c_master_write_byte(chain, (CO2_ADDR << 1) | I2C_MASTER_READ, true)); // TODO expect ack?
TRY(i2c_master_read(chain, &dummy, 1, I2C_MASTER_LAST_NACK));
TRY(i2c_master_stop(chain));
suc = i2c_master_cmd_begin(CO2_I2C_NUM, chain, pdMS_TO_TICKS(80));
i2c_cmd_link_delete(chain);
if (suc == ESP_OK) {
ESP_LOGD(TAG, "Woken up at try %d", i);
return true;
}
}
ESP_LOGE(TAG, "Fail to wake up");
return false;
#undef TRY
}
void co2_read_task(void *param) {
esp_err_t rv;
static esp_err_t do_reg_read(uint8_t reg_addr, uint8_t *reg_data, uint32_t length, uint32_t timeout_ms) {
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_reg_read(cmd, CO2_ADDR, reg_addr, reg_data, length);
esp_err_t ret = i2c_master_cmd_begin(CO2_I2C_NUM, cmd, pdMS_TO_TICKS(TIMEOUT_MS));
i2c_cmd_link_delete(cmd);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "I2C read error: %s, devaddr %x, reg %d, len %d", esp_err_to_name(ret), CO2_ADDR, reg_addr, length);
}
ESP_LOG_BUFFER_HEXDUMP(TAG, reg_data, length, ESP_LOG_DEBUG);
return ret;
}
vTaskDelay(pdMS_TO_TICKS(500));
static esp_err_t reg_read(uint8_t reg_addr, uint8_t *reg_data, uint32_t length, uint32_t timeout_ms) {
// BaseType_t suc = xSemaphoreTake(g_mux_i2c, pdMS_TO_TICKS(SEMA_TIMEOUT_MS));
// if (suc != pdPASS) {
// ESP_LOGE(TAG, "Sema fail");
// return ESP_ERR_TIMEOUT;
// }
if (!wake_up()) {
// xSemaphoreGive(g_mux_i2c);
return ESP_ERR_TIMEOUT;
}
esp_err_t rv = do_reg_read(reg_addr, reg_data, length, timeout_ms);
// xSemaphoreGive(g_mux_i2c);
// ets_delay_us(12000);
return rv;
}
// TODO
static esp_err_t do_reg_write(uint8_t reg_addr, const uint8_t *reg_data, uint32_t length, uint32_t timeout_ms) {
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_reg_write(cmd, CO2_ADDR, reg_addr, reg_data, length);
esp_err_t ret = i2c_master_cmd_begin(CO2_I2C_NUM, cmd, pdMS_TO_TICKS(timeout_ms));
i2c_cmd_link_delete(cmd);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "I2C write error: %s, devaddr %x, reg %d, len %d", esp_err_to_name(ret), CO2_ADDR, reg_addr, length);
}
return ret;
}
uint8_t buffer[128];
static esp_err_t reg_write(uint8_t reg_addr, const uint8_t *reg_data, uint32_t length, uint32_t timeout_ms) {
// BaseType_t suc = xSemaphoreTake(g_mux_i2c, pdMS_TO_TICKS(SEMA_TIMEOUT_MS));
// if (suc != pdPASS) {
// ESP_LOGE(TAG, "Sema fail");
// return ESP_ERR_TIMEOUT;
// }
if (!wake_up()) {
// xSemaphoreGive(g_mux_i2c);
return ESP_ERR_TIMEOUT;
}
esp_err_t rv = do_reg_write(reg_addr, reg_data, length, timeout_ms);
// xSemaphoreGive(g_mux_i2c);
// ets_delay_us(12000);
return rv;
}
while (1) {
void co2_read_task(void *param) {
// i2c is protected by a semaphore inside the driver, no need to worry about it here
vTaskDelay(pdMS_TO_TICKS(2000));
// continuous is the default
vTaskDelay(pdMS_TO_TICKS(500));
uint16_t ppm = 0;
uint8_t pld[] = {0x00, 0x05, 0x00, 0x05};
if (ESP_OK == reg_write(0x96, pld, 4, TIMEOUT_MS)) {
ESP_LOGI(TAG, "CO2 init OK");
}
int i = 0;
buffer[i++] = 0x68;
buffer[i++] = 0x04;
buffer[i++] = 0x00;
buffer[i++] = 0x00;
buffer[i++] = 0x00;
buffer[i++] = 0x04;
buffer[i++] = 0xf8;
buffer[i++] = 0xf0;
bool init_suc = false;
vTaskDelay(pdMS_TO_TICKS(250));
while (1) {
uart_write_bytes(CO2_UART_NUM, buffer, i);
i = uart_read_bytes(CO2_UART_NUM, buffer, 13, pdMS_TO_TICKS(1000));
vTaskDelay(pdMS_TO_TICKS(2000));
if (i != 13) {
ESP_LOGE(TAG, "Rx failed");
continue;
}
uint8_t data[4] = {};
ESP_LOG_BUFFER_HEXDUMP(TAG, buffer, i, ESP_LOG_DEBUG);
continue;
for (int retry = 0; retry < 3; retry++) {
if (ESP_OK == reg_read(0x06, data, 4, TIMEOUT_MS)) {
uint16_t filtered = (data[0] << 8) | data[1];
uint16_t unfiltered = (data[2] << 8) | data[3];
ESP_LOGI(TAG, "CO2 ppm %d, raw %d", filtered, unfiltered);
ESP_LOGI(TAG, "CO2 ppm %d", ppm);
if (pdPASS == xSemaphoreTake(g_mux_data_report, pdMS_TO_TICKS(750))) {
if (filtered > 400 && filtered < 5000) {
g_data_report.co2_ppm = (float) filtered;
if (ppm > 400 && ppm < 5000) {
g_data_report.co2_ppm = (float) ppm;
g_data_report.co2_ready = true;
g_data_report.co2_timestamp = xTaskGetTickCount();
} else {
@ -155,10 +71,5 @@ void co2_read_task(void *param) {
}
}
xSemaphoreGive(g_mux_data_report);
break;
}
vTaskDelay(pdMS_TO_TICKS(50));
}
}
}

@ -0,0 +1,164 @@
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#include "co2_sensor.h"
#include <esp_log.h>
#include <esp_err.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <driver/i2c.h>
#include <sys/time.h>
#include <hal/i2c_hal.h>
#include "voc_sensor.h"
#include "i2c_utils.h"
#include "periph_init.h"
#include "data_report.h"
static const char *TAG = "co2";
#define CO2_ADDR 104
#define CO2_I2C_NUM I2C_NUM_1
#define TIMEOUT_MS 500
// this works, but the bus is fucked up
bool wake_up() {
esp_err_t suc;
#define TRY(x) suc=x; if(suc!=ESP_OK) return suc;
for (int i = 0; i < 5; i++) {
ESP_LOGD(TAG, "Wake up try %d", i);
uint8_t dummy;
i2c_cmd_handle_t chain;
chain = i2c_cmd_link_create();
TRY(i2c_master_start(chain));
TRY(i2c_master_write_byte(chain, (CO2_ADDR << 1) | I2C_MASTER_WRITE, false));
TRY(i2c_master_write_byte(chain, 0x30, false));
TRY(i2c_master_start(chain));
TRY(i2c_master_write_byte(chain, (CO2_ADDR << 1) | I2C_MASTER_READ, false)); // TODO expect ack?
TRY(i2c_master_read(chain, &dummy, 1, I2C_MASTER_LAST_NACK));
TRY(i2c_master_stop(chain));
i2c_master_cmd_begin(CO2_I2C_NUM, chain, pdMS_TO_TICKS(70));
i2c_cmd_link_delete(chain);
chain = i2c_cmd_link_create();
TRY(i2c_master_start(chain));
TRY(i2c_master_write_byte(chain, (CO2_ADDR << 1) | I2C_MASTER_WRITE, false));
TRY(i2c_master_write_byte(chain, 0x30, false));
TRY(i2c_master_start(chain));
TRY(i2c_master_write_byte(chain, (CO2_ADDR << 1) | I2C_MASTER_READ, true)); // TODO expect ack?
TRY(i2c_master_read(chain, &dummy, 1, I2C_MASTER_LAST_NACK));
TRY(i2c_master_stop(chain));
suc = i2c_master_cmd_begin(CO2_I2C_NUM, chain, pdMS_TO_TICKS(80));
i2c_cmd_link_delete(chain);
if (suc == ESP_OK) {
ESP_LOGD(TAG, "Woken up at try %d", i);
return true;
}
}
ESP_LOGE(TAG, "Fail to wake up");
return false;
#undef TRY
}
static esp_err_t do_reg_read(uint8_t reg_addr, uint8_t *reg_data, uint32_t length, uint32_t timeout_ms) {
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_reg_read(cmd, CO2_ADDR, reg_addr, reg_data, length);
esp_err_t ret = i2c_master_cmd_begin(CO2_I2C_NUM, cmd, pdMS_TO_TICKS(TIMEOUT_MS));
i2c_cmd_link_delete(cmd);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "I2C read error: %s, devaddr %x, reg %d, len %d", esp_err_to_name(ret), CO2_ADDR, reg_addr, length);
}
ESP_LOG_BUFFER_HEXDUMP(TAG, reg_data, length, ESP_LOG_DEBUG);
return ret;
}
static esp_err_t reg_read(uint8_t reg_addr, uint8_t *reg_data, uint32_t length, uint32_t timeout_ms) {
// BaseType_t suc = xSemaphoreTake(g_mux_i2c, pdMS_TO_TICKS(SEMA_TIMEOUT_MS));
// if (suc != pdPASS) {
// ESP_LOGE(TAG, "Sema fail");
// return ESP_ERR_TIMEOUT;
// }
if (!wake_up()) {
// xSemaphoreGive(g_mux_i2c);
return ESP_ERR_TIMEOUT;
}
esp_err_t rv = do_reg_read(reg_addr, reg_data, length, timeout_ms);
// xSemaphoreGive(g_mux_i2c);
// ets_delay_us(12000);
return rv;
}
static esp_err_t do_reg_write(uint8_t reg_addr, const uint8_t *reg_data, uint32_t length, uint32_t timeout_ms) {
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_reg_write(cmd, CO2_ADDR, reg_addr, reg_data, length);
esp_err_t ret = i2c_master_cmd_begin(CO2_I2C_NUM, cmd, pdMS_TO_TICKS(timeout_ms));
i2c_cmd_link_delete(cmd);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "I2C write error: %s, devaddr %x, reg %d, len %d", esp_err_to_name(ret), CO2_ADDR, reg_addr, length);
}
return ret;
}
static esp_err_t reg_write(uint8_t reg_addr, const uint8_t *reg_data, uint32_t length, uint32_t timeout_ms) {
// BaseType_t suc = xSemaphoreTake(g_mux_i2c, pdMS_TO_TICKS(SEMA_TIMEOUT_MS));
// if (suc != pdPASS) {
// ESP_LOGE(TAG, "Sema fail");
// return ESP_ERR_TIMEOUT;
// }
if (!wake_up()) {
// xSemaphoreGive(g_mux_i2c);
return ESP_ERR_TIMEOUT;
}
esp_err_t rv = do_reg_write(reg_addr, reg_data, length, timeout_ms);
// xSemaphoreGive(g_mux_i2c);
// ets_delay_us(12000);
return rv;
}
void co2_read_task(void *param) {
// i2c is protected by a semaphore inside the driver, no need to worry about it here
// continuous is the default
vTaskDelay(pdMS_TO_TICKS(500));
uint8_t pld[] = {0x00, 0x05, 0x00, 0x05};
if (ESP_OK == reg_write(0x96, pld, 4, TIMEOUT_MS)) {
ESP_LOGI(TAG, "CO2 init OK");
}
bool init_suc = false;
vTaskDelay(pdMS_TO_TICKS(250));
while (1) {
vTaskDelay(pdMS_TO_TICKS(2000));
uint8_t data[4] = {};
for (int retry = 0; retry < 3; retry++) {
if (ESP_OK == reg_read(0x06, data, 4, TIMEOUT_MS)) {
uint16_t filtered = (data[0] << 8) | data[1];
uint16_t unfiltered = (data[2] << 8) | data[3];
ESP_LOGI(TAG, "CO2 ppm %d, raw %d", filtered, unfiltered);
if (pdPASS == xSemaphoreTake(g_mux_data_report, pdMS_TO_TICKS(750))) {
if (filtered > 400 && filtered < 5000) {
g_data_report.co2_ppm = (float) filtered;
g_data_report.co2_ready = true;
g_data_report.co2_timestamp = xTaskGetTickCount();
} else {
g_data_report.co2_ready = false;
}
}
xSemaphoreGive(g_mux_data_report);
break;
}
vTaskDelay(pdMS_TO_TICKS(50));
}
}
}

@ -3,6 +3,8 @@
#include "driver/i2c.h"
#include "data_report.h"
#include <driver/uart.h>
static const char *TAG = "periph_init";
esp_err_t periph_init() {
@ -38,6 +40,7 @@ esp_err_t periph_init() {
// senseair i2c
{
#if 0
int i2c_master_port = I2C_NUM_1;
i2c_config_t conf2 = {
.mode = I2C_MODE_MASTER,
@ -57,6 +60,60 @@ esp_err_t periph_init() {
ESP_LOGE(TAG, "Err in i2c_driver_install");
return rv;
}
#else
const uart_config_t uart_config = {
.baud_rate = 9600,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.use_ref_tick = true
};
ESP_ERROR_CHECK( uart_param_config(UART_NUM_1, &uart_config) );
// Set UART pins(TX: IO17 (UART2 default), RX: IO16 (UART2 default), RTS: IO18, CTS: IO19)
ESP_ERROR_CHECK(uart_set_pin(UART_NUM_2,
CONFIG_PIN_I2C_SCL1,
CONFIG_PIN_I2C_SDA1,
UART_PIN_NO_CHANGE,
UART_PIN_NO_CHANGE));
ESP_ERROR_CHECK(uart_driver_install(UART_NUM_1,
/* rxbuf */ 256, /* txbuf */ 0, /* que */ 0, /* uart que */ NULL, /* alloc flags */ 0));
#endif
}
// outputs
{
gpio_config_t gpioconf = {
.pin_bit_mask = (1 << CONFIG_PIN_CO2_COMSEL) | (1 << CONFIG_PIN_CO2_EN),
.mode = GPIO_MODE_OUTPUT,
};
rv = gpio_config(&gpioconf);
if (rv != ESP_OK) {
ESP_LOGE(TAG, "Err in gpio_config");
return rv;
}
gpio_set_level(CONFIG_PIN_CO2_COMSEL, 0); // low=I2C
gpio_set_level(CONFIG_PIN_CO2_EN, 1); // active high
}
// input
{
gpio_config_t gpioconf = {
.pin_bit_mask = (1 << CONFIG_PIN_CO2_NRDY),
.mode = GPIO_MODE_INPUT,
.pull_up_en = 1,
.pull_down_en = 0,
.intr_type = GPIO_INTR_DISABLE,
};
rv = gpio_config(&gpioconf);
if (rv != ESP_OK) {
ESP_LOGE(TAG, "Err in gpio_config");
return rv;
}
}
return ESP_OK;

@ -63,6 +63,7 @@ CONFIG_BOOTLOADER_WDT_TIME_MS=9000
# CONFIG_BOOTLOADER_SKIP_VALIDATE_ALWAYS is not set
CONFIG_BOOTLOADER_RESERVE_RTC_SIZE=0
# CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC is not set
CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT=y
# end of Bootloader config
#
@ -136,6 +137,9 @@ CONFIG_PIN_I2C_SDA0=16
CONFIG_PIN_I2C_SCL0=17
CONFIG_PIN_I2C_SDA1=5
CONFIG_PIN_I2C_SCL1=18
CONFIG_PIN_CO2_NRDY=23
CONFIG_PIN_CO2_COMSEL=13
CONFIG_PIN_CO2_EN=15
# end of Pin mapping
#
@ -480,6 +484,7 @@ CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT=y
# CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT is not set
# CONFIG_ESP_SYSTEM_PANIC_GDBSTUB is not set
CONFIG_ESP_SYSTEM_PD_FLASH=y
# CONFIG_ESP_SYSTEM_FLASH_LEAKAGE_WORKAROUND is not set
#
# Memory protection
@ -599,6 +604,10 @@ CONFIG_FMB_SERIAL_BUF_SIZE=256
CONFIG_FMB_SERIAL_ASCII_BITS_PER_SYMB=8
CONFIG_FMB_SERIAL_ASCII_TIMEOUT_RESPOND_MS=1000
CONFIG_FMB_PORT_TASK_PRIO=10
# CONFIG_FMB_PORT_TASK_AFFINITY_NO_AFFINITY is not set
CONFIG_FMB_PORT_TASK_AFFINITY_CPU0=y
# CONFIG_FMB_PORT_TASK_AFFINITY_CPU1 is not set
CONFIG_FMB_PORT_TASK_AFFINITY=0x0
CONFIG_FMB_CONTROLLER_SLAVE_ID_SUPPORT=y
CONFIG_FMB_CONTROLLER_SLAVE_ID=0x00112233
CONFIG_FMB_CONTROLLER_NOTIFY_TIMEOUT=20
@ -608,6 +617,8 @@ CONFIG_FMB_EVENT_QUEUE_TIMEOUT=20
CONFIG_FMB_TIMER_PORT_ENABLED=y
CONFIG_FMB_TIMER_GROUP=0
CONFIG_FMB_TIMER_INDEX=0
CONFIG_FMB_MASTER_TIMER_GROUP=0
CONFIG_FMB_MASTER_TIMER_INDEX=0
# CONFIG_FMB_TIMER_ISR_IN_IRAM is not set
# end of Modbus configuration

Loading…
Cancel
Save