co2 maybe working better

modbus
Ondřej Hruška 11 months ago
parent 7c1c0203c0
commit 3fd1c73c7b
  1. 2
      main/CMakeLists.txt
  2. 5
      main/app_main.c
  3. 215
      main/co2_sensor.c
  4. 6
      main/data_report.h
  5. 98
      main/modbus_crc.c
  6. 37
      main/modbus_crc.h
  7. 167
      main/modbus_fn.c
  8. 85
      main/modbus_fn.h
  9. 30
      main/periph_init.c
  10. 8
      main/settings.c
  11. 3
      main/settings.h
  12. 36
      main/voc_sensor.c
  13. 8
      sdkconfig

@ -8,6 +8,8 @@ idf_component_register(SRCS
voc_sensor.c
co2_sensor.c
data_report.c
modbus_crc.c
modbus_fn.c
periph_init.c
i2c_utils.c
console/console_ioimpl.c

@ -59,9 +59,8 @@ void app_main(void) {
vTaskDelay(pdMS_TO_TICKS(1000));
//xTaskCreatePinnedToCore(voc_read_task, "VOC", 4096, NULL, PRIO_NORMAL, NULL, 1);
xTaskCreatePinnedToCore(co2_read_task, "CO2", 4096, NULL, PRIO_NORMAL, NULL, 1);
// xTaskCreate(co2_read_task, "CO2", 4096, NULL, PRIO_NORMAL, NULL);
xTaskCreate(voc_read_task, "VOC", 4096, NULL, PRIO_NORMAL, NULL);
xTaskCreate(co2_read_task, "CO2", 4096, NULL, PRIO_NORMAL, NULL);
console_init(NULL);
register_console_commands();

@ -1,4 +1,4 @@
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
//#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#include "co2_sensor.h"
@ -7,13 +7,12 @@
#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"
#include "modbus_fn.h"
#include "settings.h"
#include <driver/uart.h>
#include <string.h>
static const char *TAG = "co2";
@ -21,49 +20,185 @@ static const char *TAG = "co2";
#define CO2_UART_NUM 1
#define TIMEOUT_MS 500
static int mb_write_and_read(uint8_t *buffer, int num, size_t resplen) {
if (num < 0) {
ESP_LOGE(TAG, "MB build failed");
return 0;
}
ESP_LOGD(TAG, "Sending");
ESP_LOG_BUFFER_HEXDUMP(TAG, buffer, num, ESP_LOG_DEBUG);
uart_write_bytes(CO2_UART_NUM, buffer, num);
ESP_LOGD(TAG, "Expect resp of %d bytes", resplen);
num = uart_read_bytes(CO2_UART_NUM, buffer, resplen, pdMS_TO_TICKS(500));
ESP_LOGD(TAG, "Received %d bytes", num);
ESP_LOG_BUFFER_HEXDUMP(TAG, buffer, num, ESP_LOG_DEBUG);
return num;
}
static void write_saved_calib_to_sensor() {
static uint8_t buffer[40];
size_t resplen;
int num;
int resp;
if (g_Settings.co2_calib[0] > 0) {
ESP_LOGI(TAG, "Writing saved calib to sensor");
/* Write calib */
resplen = 0;
num = mb_build_fc16(buffer, 128, &resplen, 104, 4, g_Settings.co2_calib, 5);
num = mb_write_and_read(buffer, num, resplen);
resp = mb_parse_fc16(buffer, num);
if (resp < 0) {
ESP_LOGE(TAG, "Fail to write calib constants!");
}
} else {
ESP_LOGW(TAG, "No saved calib to write to sensor!");
}
}
void co2_restart(bool restore_calib) {
ESP_LOGI(TAG, "Restarting CO2 sensor");
gpio_set_level(CONFIG_PIN_CO2_EN, 0);
vTaskDelay(pdMS_TO_TICKS(100));
gpio_set_level(CONFIG_PIN_CO2_EN, 1);
vTaskDelay(pdMS_TO_TICKS(50));
if (restore_calib) {
write_saved_calib_to_sensor();
}
}
static bool ppm_looks_valid(uint16_t ppm) {
return ppm > 400 && ppm < 5000;
}
void co2_read_task(void *param) {
esp_err_t rv;
vTaskDelay(pdMS_TO_TICKS(500));
// TODO
uint8_t buffer[128];
static uint8_t buffer[128];
static uint16_t values[32];
int qty;
size_t resplen;
int num;
write_saved_calib_to_sensor();
const uint32_t read_cycle_time_ticks = pdMS_TO_TICKS(10 * 1000);
const uint32_t calib_persist_time_ticks = pdMS_TO_TICKS(6 * 3600 * 1000);
uint32_t last_calib_persist = xTaskGetTickCount();
ESP_LOGD(TAG, "Calib persist time = %d ticks", calib_persist_time_ticks);
int num_fails = 0;
uint16_t ppm = 0;
while (1) {
vTaskDelay(pdMS_TO_TICKS(2000));
uint16_t ppm = 0;
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;
ESP_LOGI(TAG, "send %d bytes", i);
uart_write_bytes(CO2_UART_NUM, buffer, i);
i = uart_read_bytes(CO2_UART_NUM, buffer, 13, pdMS_TO_TICKS(1000));
ESP_LOGI(TAG, "received %d bytes", i);
ESP_LOG_BUFFER_HEXDUMP(TAG, buffer, i, ESP_LOG_DEBUG);
if (i != 13) {
ESP_LOGE(TAG, "Rx failed");
continue;
}
continue;
ppm = 0;
vTaskDelay(read_cycle_time_ticks);
if (num_fails > 10) {
ESP_LOGW(TAG, "Too many CO2 fails, restarting sensor");
co2_restart(true);
num_fails = 0;
}
resplen = 0;
num = mb_build_fc4(buffer, 128, &resplen, 104, 0, 5);
num = mb_write_and_read(buffer, num, resplen);
qty = mb_parse_fc4(buffer, num, values, 32);
if (qty < 0) {
num_fails++;
goto next;
}
// Read was OK, reset the fail counter
num_fails = 0;
uint16_t status = values[0];
bool co2_needs_restart = false;
bool calibration_error = false;
if (status != 0) {
if (status & 0x01) {
ESP_LOGE(TAG, "CO2 fatal error");
co2_needs_restart = true;
}
if (status & 0x02) {
ESP_LOGE(TAG, "CO2 I2C communication error");
}
if (status & 0x04) {
ESP_LOGE(TAG, "CO2 internal I2C operation error");
}
if (status & 0x08) {
ESP_LOGE(TAG, "CO2 calibration error");
calibration_error = true;
}
if (status & 0x10) {
ESP_LOGE(TAG, "CO2 self-diagnostics error");
co2_needs_restart = true;
}
if (status & 0x20) {
ESP_LOGE(TAG, "CO2 out of range error");
// nonsense, this means calibration failed
calibration_error = true;
}
if (status & 0x40) {
ESP_LOGE(TAG, "CO2 memory error");
co2_needs_restart = true;
}
if (status & 0x80) {
ESP_LOGE(TAG, "CO2 external I2C error");
}
if (co2_needs_restart) {
co2_restart(!calibration_error);
if (calibration_error) {
// Prevent persisting values too soon, give it some time to calibrate
last_calib_persist = xTaskGetTickCount();
}
}
goto next;
}
ppm = values[3];
ESP_LOGI(TAG, "CO2 ppm %d", ppm);
if (!ppm_looks_valid(ppm)) {
ESP_LOGW(TAG, "CO2 measures nonsense!");
co2_restart(true);
} else {
// CO2 measurement looks OK
uint32_t tickNow = xTaskGetTickCount();
if ((tickNow - last_calib_persist) > calib_persist_time_ticks) {
ESP_LOGI(TAG, "Read & persist CO2 calibration");
last_calib_persist = tickNow;
resplen = 0;
num = mb_build_fc3(buffer, 128, &resplen, 104, 4, 5);
num = mb_write_and_read(buffer, num, resplen);
qty = mb_parse_fc3(buffer, num, values, 32);
if (qty < 0) {
num_fails++;
}
if (qty == 5) {
memcpy(g_Settings.co2_calib, values, 5 * 2);
settings_persist(SETTINGS_co2_calib);
}
}
}
next:
if (pdPASS == xSemaphoreTake(g_mux_data_report, pdMS_TO_TICKS(750))) {
if (ppm > 400 && ppm < 5000) {
if (ppm_looks_valid(ppm)) {
g_data_report.co2_ppm = (float) ppm;
g_data_report.co2_ready = true;
g_data_report.co2_timestamp = xTaskGetTickCount();

@ -15,14 +15,14 @@
struct data_report {
bool iaq_ready;
uint32_t iaq_timestamp;
uint32_t iaq_timestamp; // timestamp in ticks
float iaq;
float iaq_static;
float iaq_co2_ppm_equiv;
float iaq_voc_ppm_equiv;
bool thpg_ready;
uint32_t thpg_timestamp;
uint32_t thpg_timestamp; // timestamp in ticks
float temperature;
float temperature_raw;
float pressure;
@ -31,7 +31,7 @@ struct data_report {
float gas_raw;
bool co2_ready;
uint32_t co2_timestamp;
uint32_t co2_timestamp; // timestamp in ticks
float co2_ppm;
};

@ -0,0 +1,98 @@
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbcrc.c,v 1.7 2007/02/18 23:50:27 wolti Exp $
*/
/* ----------------------- Platform includes --------------------------------*/
#include "modbus_crc.h"
#include <stdint.h>
static const uint8_t aucCRCHi[] = {
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40
};
static const uint8_t aucCRCLo[] = {
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7,
0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E,
0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9,
0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,
0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32,
0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D,
0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38,
0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF,
0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1,
0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,
0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB,
0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA,
0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,
0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97,
0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E,
0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89,
0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83,
0x41, 0x81, 0x80, 0x40
};
uint16_t modbus_crc(const uint8_t *pucFrame, uint16_t usLen)
{
uint8_t ucCRCHi = 0xFF;
uint8_t ucCRCLo = 0xFF;
int iIndex;
while (usLen--) {
iIndex = ucCRCLo ^ *(pucFrame++);
ucCRCLo = (uint8_t) (ucCRCHi ^ aucCRCHi[iIndex]);
ucCRCHi = aucCRCLo[iIndex];
}
// return (uint16_t) (ucCRCHi << 8 | ucCRCLo);
return (uint16_t) (ucCRCLo << 8 | ucCRCHi);
}

@ -0,0 +1,37 @@
/*
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* File: $Id: mbcrc.h,v 1.5 2006/12/07 22:10:34 wolti Exp $
*/
#ifndef _MODBUS_CRC_H
#define _MODBUS_CRC_H
#include <stdint.h>
uint16_t modbus_crc(const uint8_t *pucFrame, uint16_t usLen);
#endif

@ -0,0 +1,167 @@
#include <assert.h>
#include "modbus_fn.h"
#include "modbus_crc.h"
#include <esp_log.h>
static const char *TAG = "mb";
static bool crc_matches(const uint8_t *buf, size_t len) {
assert(buf);
uint16_t real = modbus_crc(buf, len - 2);
uint16_t given = ((uint16_t) buf[len - 2] << 8) | (uint16_t) buf[len - 1];
return real == given;
}
// Read holding registers
int mb_build_fc3(uint8_t *buf, size_t len, size_t *resplen, uint8_t mbAddr, uint16_t ref, uint16_t qty) {
assert(buf);
assert(resplen);
if (len < 8) {
ESP_LOGE(TAG, "Buf len too short for FC3");
return -1;
}
if (qty > 127) {
ESP_LOGE(TAG, "FC3 qty too high");
return -1;
}
size_t wp = 0;
buf[wp++] = mbAddr;
buf[wp++] = 0x03;
buf[wp++] = (ref & 0xFF00) >> 8;
buf[wp++] = (ref & 0xFF);
buf[wp++] = (qty & 0xFF00) >> 8;
buf[wp++] = (qty & 0xFF);
uint16_t crc = modbus_crc(buf, wp);
buf[wp++] = (crc & 0xFF00) >> 8;
buf[wp++] = (crc & 0xFF);
*resplen = 5 + qty * 2;
return (int) wp;
}
static int mb_parse_fc3_4(const uint8_t *buf, size_t len, uint16_t *dst, size_t capacity, int fcX) {
assert(buf);
if (!crc_matches(buf, len)) {
ESP_LOGE(TAG, "FC%d CRC mismatch!", fcX);
return -1;
}
//11 03 06 AE41 5652 4340 49AD
if (len == 5 && buf[1] == (0x80+fcX)) {
ESP_LOGE(TAG, "FC%d exception: %d", fcX, buf[2]);
return -1;
}
if (buf[1] != fcX) {
ESP_LOGE(TAG, "FC%d decode fail: not FC%d", fcX, fcX);
return -1;
}
int num = buf[2] / 2; // this is the number of bytes
if (capacity < num) {
ESP_LOGE(TAG, "FC%d decode fail: dst too small for %d registers", fcX, num);
return -1;
}
for (int i = 0; i < num; i++) {
*dst++ = (((uint16_t) buf[3 + i * 2]) << 8) | (((uint16_t) buf[3 + i * 2 + 1]));
}
return num;
}
int mb_parse_fc3(const uint8_t *buf, size_t len, uint16_t *dst, size_t capacity) {
return mb_parse_fc3_4(buf, len, dst, capacity, 3);
}
//// Read input registers
int mb_build_fc4(uint8_t *buf, size_t len, size_t *resplen, uint8_t mbAddr, uint16_t ref, uint16_t qty) {
assert(buf);
assert(resplen);
if (len < 8) {
ESP_LOGE(TAG, "Buf len too short for FC4");
return -1;
}
if (qty > 127) {
ESP_LOGE(TAG, "FC3 qty too high");
return -1;
}
size_t wp = 0;
buf[wp++] = mbAddr;
buf[wp++] = 0x04;
buf[wp++] = (ref & 0xFF00) >> 8;
buf[wp++] = (ref & 0xFF);
buf[wp++] = (qty & 0xFF00) >> 8;
buf[wp++] = (qty & 0xFF);
uint16_t crc = modbus_crc(buf, wp);
buf[wp++] = (crc & 0xFF00) >> 8;
buf[wp++] = (crc & 0xFF);
*resplen = 5 + qty * 2;
return (int) wp;
}
int mb_parse_fc4(const uint8_t *buf, size_t len, uint16_t *dst, size_t capacity) {
return mb_parse_fc3_4(buf, len, dst, capacity, 4);
}
// Write holding registers
int mb_build_fc16(uint8_t *buf, size_t len, size_t *resplen, uint8_t mbAddr, uint16_t ref, const uint16_t *data, uint16_t qty) {
assert(buf);
assert(data);
assert(resplen);
if (len < 9 + qty * 2) {
ESP_LOGE(TAG, "Buf len too short for FC16, or bad len");
return -1;
}
if (qty > 127) {
ESP_LOGE(TAG, "FC16 qty too high");
return -1;
}
size_t wp = 0;
buf[wp++] = mbAddr;
buf[wp++] = 0x10;
buf[wp++] = (ref & 0xFF00) >> 8;
buf[wp++] = (ref & 0xFF);
buf[wp++] = (qty & 0xFF00) >> 8;
buf[wp++] = (qty & 0xFF);
buf[wp++] = qty * 2;
for (int i = 0; i < qty; i++) {
buf[wp++] = (data[i] & 0xFF00) >> 8;
buf[wp++] = (data[i] & 0xFF);
}
uint16_t crc = modbus_crc(buf, wp);
buf[wp++] = (crc & 0xFF00) >> 8;
buf[wp++] = (crc & 0xFF);
*resplen = 8;
return (int) wp;
}
bool mb_parse_fc16(const uint8_t *buf, size_t len)
{
assert(buf);
if (!crc_matches(buf, len)) {
ESP_LOGE(TAG, "FC16 CRC mismatch!");
return -1;
}
//11 10 0001 0002 1298
if (len == 5 && buf[1] == 0x90) {
ESP_LOGE(TAG, "FC16 exception: %d", buf[2]);
return -1;
}
if (buf[1] != 16) {
ESP_LOGE(TAG, "FC16 decode fail: not FC16");
return -1;
}
return 0;
}

@ -0,0 +1,85 @@
/**
* TODO file description
*
* Created on 2022/01/02.
*/
#ifndef ESPNODE_MODBUS_FN_H
#define ESPNODE_MODBUS_FN_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
/**
* Build "read holding registers" packet
*
* @param buf buf to write to
* @param len buf len
* @param resplen expected response length (if not exception)
* @param mbAddr mb address
* @param ref first register number
* @param qty register quantity
* @return number of buf bytes if success, negative if error
*/
int mb_build_fc3(uint8_t *buf, size_t len, size_t *resplen, uint8_t mbAddr, uint16_t ref, uint16_t qty);
/**
* Parse "read holding registers" response
*
* @param buf buf with the response bytes
* @param len response buf len
* @param dst store results here
* @param capacity capacity of "dst"
* @return num of results, negative if error
*/
int mb_parse_fc3(const uint8_t *buf, size_t len, uint16_t *dst, size_t capacity);
/**
* Build "read input registers" packet
*
* @param buf buf to write to
* @param len buf len
* @param resplen expected response length (if not exception)
* @param mbAddr mb address
* @param ref first register number
* @param qty register quantity
* @return number of buf bytes if success, negative if error
*/
int mb_build_fc4(uint8_t *buf, size_t len, size_t *resplen, uint8_t mbAddr, uint16_t ref, uint16_t qty);
/**
* Parse "read input registers" response
*
* @param buf buf with the response bytes
* @param len response buf len
* @param dst store results here
* @param capacity capacity of "dst"
* @return num of results, negative if error
*/
int mb_parse_fc4(const uint8_t *buf, size_t len, uint16_t *dst, size_t capacity);
/**
* Build "write holding registers" packet
*
* @param buf buf to write to
* @param len buf len
* @param resplen expected response length (if not exception)
* @param mbAddr mb address
* @param ref first register number
* @param data register data to write, as an array
* @param qty register quantity
* @return number of buf bytes if success, negative if error
*/
int mb_build_fc16(uint8_t *buf, size_t len, size_t *resplen, uint8_t mbAddr, uint16_t ref, const uint16_t *data, uint16_t qty);
/**
* Parse "write holding registers" response
*
* @param buf buf with the response bytes
* @param len response buf len
* @return 0 if success, negative if error
*/
bool mb_parse_fc16(const uint8_t *buf, size_t len);
#endif //ESPNODE_MODBUS_FN_H

@ -62,7 +62,7 @@ esp_err_t periph_init() {
}
#else
int i2c_co2_port = I2C_NUM_1;
const uart_config_t uart_config = {
.baud_rate = 9600,
.data_bits = UART_DATA_8_BITS,
@ -72,38 +72,40 @@ esp_err_t periph_init() {
.source_clk = UART_SCLK_APB,
//.use_ref_tick = true
};
ESP_ERROR_CHECK(uart_driver_install(i2c_co2_port,
/* rxbuf */ 256, /* txbuf */ 256, /* que */ 0, /* uart que */ NULL, /* alloc flags */ 0));
ESP_ERROR_CHECK( uart_param_config(i2c_co2_port, &uart_config) );
ESP_ERROR_CHECK(uart_set_pin(i2c_co2_port,
// CONFIG_PIN_I2C_SCL1,
// CONFIG_PIN_I2C_SDA1,
CONFIG_PIN_I2C_SDA1,
CONFIG_PIN_I2C_SCL1,
UART_PIN_NO_CHANGE,
UART_PIN_NO_CHANGE));
// Set UART pins(TX, RX, RTS, CTS)
ESP_ERROR_CHECK(uart_set_pin(i2c_co2_port,
CONFIG_PIN_I2C_SCL1,
CONFIG_PIN_I2C_SDA1,
UART_PIN_NO_CHANGE,
UART_PIN_NO_CHANGE));
#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, 1); // low=I2C
gpio_set_level(CONFIG_PIN_CO2_EN, 1); // active high
}
// input
{
gpio_config_t gpioconf = {
@ -113,7 +115,7 @@ esp_err_t periph_init() {
.pull_down_en = 0,
.intr_type = GPIO_INTR_DISABLE,
};
rv = gpio_config(&gpioconf);
if (rv != ESP_OK) {
ESP_LOGE(TAG, "Err in gpio_config");

@ -118,6 +118,10 @@ void settings_load(void)
NVSCHECK(nvs_get_str(storage, "ntp_srv", g_Settings.ntp_srv, &capacity));
ensure_str_terminated(g_Settings.ntp_srv, NTP_SRV_LEN);
}
{
size_t capacity = 10;
NVSCHECK(nvs_get_blob(storage, "co2_calib", g_Settings.co2_calib, &capacity));
}
}
void settings_persist(enum settings_key_enum what)
@ -152,6 +156,10 @@ void settings_persist(enum settings_key_enum what)
if (what==SETTINGS_ALL || what==SETTINGS_ntp_srv) {
NVSCHECK(nvs_set_str(storage, "ntp_srv", g_Settings.ntp_srv));
}
if (what==SETTINGS_ALL || what==SETTINGS_co2_calib) {
NVSCHECK(nvs_set_blob(storage, "co2_calib", (void*) g_Settings.co2_calib, 5*sizeof(uint16_t)));
}
}
uint16_t app_get_bootcount()

@ -20,6 +20,8 @@ extern nvs_handle g_nvs_storage;
#define NTP_SRV_LEN 32
#define DEF_NTP_SRV "pool.ntp.org"
#define CO2_CALIB_DEF ({0,0,0,0,0})
/* type , name , suffix , defval , generate load/save funcs
* , , save fn - use 'none' if unused to avoid build errors
* , , , save fn var prefix */
@ -32,6 +34,7 @@ extern nvs_handle g_nvs_storage;
X(char , console_pw, [CONSOLE_PW_LEN], "" , false , none , ) \
X(bool , ntp_enable , , 1 , true , bool , &) \
X(char , ntp_srv ,[NTP_SRV_LEN], DEF_NTP_SRV , false, none , ) \
X(uint16_t , co2_calib ,[5], {}, false, none , ) \
X(bool , dhcp_wd_enable , , 1 , true , bool , &) \
X(bool , dhcp_enable , , 1 , true , bool , &) \
X(uint32_t , static_ip , , 0 , true , u32 , &) \

@ -134,66 +134,64 @@ static void new_data_callback(
return;
}
// TODO do something with this
struct data_report my_report = {};
ESP_LOGI(TAG, "BSEC outputs: timestamp = %d", (int) (outputs->output[0].time_stamp / INT64_C(1000000)));
ESP_LOGD(TAG, "BSEC outputs: timestamp = %d", (int) (outputs->output[0].time_stamp / INT64_C(1000000)));
for (uint8_t i = 0; i < outputs->nOutputs; i++) {
const bsecData output = outputs->output[i];
switch (output.sensor_id) {
case BSEC_OUTPUT_IAQ:
ESP_LOGI(TAG, "\tIAQ = %f, accuracy %d", output.signal, (int) output.accuracy);
ESP_LOGD(TAG, " IAQ = %f, accuracy %d", output.signal, (int) output.accuracy);
my_report.iaq = output.signal;
my_report.iaq_ready = (3 == (int) output.accuracy); // calibration finished
break;
case BSEC_OUTPUT_STATIC_IAQ:
ESP_LOGI(TAG, "\tSTATIC_IAQ = %f, accuracy %d", output.signal, (int) output.accuracy);
ESP_LOGD(TAG, " STATIC_IAQ = %f, accuracy %d", output.signal, (int) output.accuracy);
my_report.iaq_static = output.signal;
break;
case BSEC_OUTPUT_CO2_EQUIVALENT:
ESP_LOGI(TAG, "\tCO2_EQUIVALENT = %f, accuracy %d", output.signal, (int) output.accuracy);
ESP_LOGD(TAG, " CO2_EQUIVALENT = %f, accuracy %d", output.signal, (int) output.accuracy);
my_report.iaq_co2_ppm_equiv = output.signal;
break;
case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT:
ESP_LOGI(TAG, "\tBREATH_VOC_EQUIVALENT = %f, accuracy %d", output.signal, (int) output.accuracy);
ESP_LOGD(TAG, " BREATH_VOC_EQUIVALENT = %f, accuracy %d", output.signal, (int) output.accuracy);
my_report.iaq_voc_ppm_equiv = output.signal;
break;
case BSEC_OUTPUT_RAW_PRESSURE:
ESP_LOGI(TAG, "\tRAW_PRESSURE = %f", output.signal);
ESP_LOGD(TAG, " RAW_PRESSURE = %f", output.signal);
my_report.pressure = output.signal;
my_report.thpg_ready = true;
break;
case BSEC_OUTPUT_RAW_TEMPERATURE:
ESP_LOGI(TAG, "\tRAW_TEMPERATURE = %f", output.signal);
ESP_LOGD(TAG, " RAW_TEMPERATURE = %f", output.signal);
my_report.temperature_raw = output.signal;
my_report.thpg_ready = true;
break;
case BSEC_OUTPUT_RAW_HUMIDITY:
ESP_LOGI(TAG, "\tRAW_HUMIDITY = %f", output.signal);
ESP_LOGD(TAG, " RAW_HUMIDITY = %f", output.signal);
my_report.humidity_raw = output.signal;
my_report.thpg_ready = true;
break;
case BSEC_OUTPUT_RAW_GAS:
ESP_LOGI(TAG, "\tRAW_GAS = %f", output.signal);
ESP_LOGD(TAG, " RAW_GAS = %f", output.signal);
my_report.gas_raw = output.signal;
my_report.thpg_ready = true;
break;
case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE:
ESP_LOGI(TAG, "\tSENSOR_HEAT_COMPENSATED_TEMPERATURE = %f", output.signal);
ESP_LOGD(TAG, " SENSOR_HEAT_COMPENSATED_TEMPERATURE = %f", output.signal);
my_report.temperature = output.signal;
my_report.thpg_ready = true;
break;
case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY:
ESP_LOGI(TAG, "\tSENSOR_HEAT_COMPENSATED_HUMIDITY = %f", output.signal);
ESP_LOGD(TAG, " SENSOR_HEAT_COMPENSATED_HUMIDITY = %f", output.signal);
my_report.humidity = output.signal;
my_report.thpg_ready = true;
break;
case BSEC_OUTPUT_STABILIZATION_STATUS:
ESP_LOGI(TAG, "\tSTABILIZATION_STATUS status = %d", (int) output.signal);
ESP_LOGD(TAG, " STABILIZATION_STATUS status = %d", (int) output.signal);
break;
case BSEC_OUTPUT_RUN_IN_STATUS:
ESP_LOGI(TAG, "\tRUN_IN_STATUS = %d", (int) output.signal);
ESP_LOGD(TAG, " RUN_IN_STATUS = %d", (int) output.signal);
break;
default:
break;
@ -202,6 +200,9 @@ static void new_data_callback(
if (pdPASS == xSemaphoreTake(g_mux_data_report, pdMS_TO_TICKS(1000))) {
if (my_report.thpg_ready) {
ESP_LOGI(TAG, "%.1f%%r.H, %.1f°C, %.0f Pa, gas %.0fR",
my_report.humidity, my_report.temperature, my_report.pressure, my_report.gas_raw);
g_data_report.thpg_ready = true;
g_data_report.thpg_timestamp = xTaskGetTickCount();
g_data_report.humidity = my_report.humidity;
@ -215,6 +216,11 @@ static void new_data_callback(
}
if (my_report.iaq_ready) {
ESP_LOGI(TAG, "IAQ %.1f, IAQ_s %.1f, CO2eq %.1f ppm, VOCeq %.1f ppm",
my_report.iaq, my_report.iaq_static,
my_report.iaq_co2_ppm_equiv,
my_report.iaq_voc_ppm_equiv);
g_data_report.iaq_ready = true;
g_data_report.iaq_timestamp = xTaskGetTickCount();
g_data_report.iaq = my_report.iaq;

@ -63,7 +63,6 @@ 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
#
@ -484,7 +483,6 @@ 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
@ -604,10 +602,6 @@ 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
@ -617,8 +611,6 @@ 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