#include "JW01.h" #include #include #include #include "driver/uart.h" #include "esp_check.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" static const char *TAG = "JW01"; static bool s_inited = false; static uint32_t s_parse_fail_count = 0; static esp_err_t jw01_parse_binary_frames(const uint8_t *buf, int len, jw01_data_t *out) { bool found = false; float last_co2 = 0.0f; for (int i = 0; i + 5 < len; ++i) { if (buf[i] != 0x2C) { continue; } uint8_t cs = (uint8_t)(buf[i] + buf[i + 1] + buf[i + 2] + buf[i + 3] + buf[i + 4]); if (cs != buf[i + 5]) { continue; } uint16_t value = ((uint16_t)buf[i + 1] << 8) | buf[i + 2]; last_co2 = (float)value; found = true; i += 5; } if (!found) { return ESP_ERR_INVALID_RESPONSE; } out->co2 = last_co2; out->co2_valid = true; return ESP_OK; } static void str_to_upper_inplace(char *s) { while (*s != '\0') { *s = (char)toupper((unsigned char)*s); ++s; } } static bool extract_float_by_key(const char *line, const char *key, float *out) { char up_line[160]; char up_key[24]; strncpy(up_line, line, sizeof(up_line) - 1); up_line[sizeof(up_line) - 1] = '\0'; strncpy(up_key, key, sizeof(up_key) - 1); up_key[sizeof(up_key) - 1] = '\0'; str_to_upper_inplace(up_line); str_to_upper_inplace(up_key); char *pos = strstr(up_line, up_key); if (pos == NULL) { return false; } while (*pos != '\0' && *pos != ':' && *pos != '=') { ++pos; } if (*pos == '\0') { return false; } ++pos; while (*pos != '\0' && (isspace((unsigned char)*pos) || *pos == ',')) { ++pos; } char *endptr = NULL; float value = strtof(pos, &endptr); if (endptr == pos) { return false; } *out = value; return true; } static esp_err_t jw01_parse_line(const char *line, jw01_data_t *out) { jw01_data_t data = {0}; data.tvoc_valid = extract_float_by_key(line, "TVOC", &data.tvoc); if (!data.tvoc_valid) { data.tvoc_valid = extract_float_by_key(line, "VOC", &data.tvoc); } data.hcho_valid = extract_float_by_key(line, "HCHO", &data.hcho); if (!data.hcho_valid) { data.hcho_valid = extract_float_by_key(line, "CH2O", &data.hcho); } data.co2_valid = extract_float_by_key(line, "CO2", &data.co2); if (!data.tvoc_valid && !data.hcho_valid && !data.co2_valid) { return ESP_ERR_INVALID_RESPONSE; } *out = data; return ESP_OK; } esp_err_t jw01_init(void) { if (s_inited) { return ESP_OK; } const uart_config_t uart_cfg = { .baud_rate = JW01_UART_BAUDRATE, .data_bits = UART_DATA_8_BITS, .parity = UART_PARITY_DISABLE, .stop_bits = UART_STOP_BITS_1, .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, .source_clk = UART_SCLK_DEFAULT, }; ESP_RETURN_ON_ERROR(uart_driver_install((uart_port_t)JW01_UART_PORT, 1024, 0, 0, NULL, 0), TAG, "uart driver install failed"); ESP_RETURN_ON_ERROR(uart_param_config((uart_port_t)JW01_UART_PORT, &uart_cfg), TAG, "uart param config failed"); ESP_RETURN_ON_ERROR(uart_set_pin((uart_port_t)JW01_UART_PORT, JW01_UART_TX_GPIO, JW01_UART_RX_GPIO, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE), TAG, "uart set pin failed"); ESP_RETURN_ON_ERROR(uart_flush_input((uart_port_t)JW01_UART_PORT), TAG, "uart flush failed"); s_inited = true; ESP_LOGI(TAG, "JW01 init done on UART%d TX=%d RX=%d baud=%d", JW01_UART_PORT, JW01_UART_TX_GPIO, JW01_UART_RX_GPIO, JW01_UART_BAUDRATE); return ESP_OK; } esp_err_t jw01_read(jw01_data_t *out_data, int timeout_ms) { ESP_RETURN_ON_FALSE(out_data != NULL, ESP_ERR_INVALID_ARG, TAG, "out_data is null"); ESP_RETURN_ON_FALSE(s_inited, ESP_ERR_INVALID_STATE, TAG, "jw01 not init"); char line[160] = {0}; int n = uart_read_bytes((uart_port_t)JW01_UART_PORT, (uint8_t *)line, sizeof(line) - 1, pdMS_TO_TICKS(timeout_ms)); if (n <= 0) { return ESP_ERR_TIMEOUT; } line[n] = '\0'; jw01_data_t data = {0}; esp_err_t ret = jw01_parse_binary_frames((const uint8_t *)line, n, &data); if (ret != ESP_OK) { ret = jw01_parse_line(line, &data); } if (ret == ESP_OK) { *out_data = data; } if (ret != ESP_OK) { s_parse_fail_count++; if (s_parse_fail_count <= 5 || (s_parse_fail_count % 20) == 0) { ESP_LOGW(TAG, "parse failed (%s), rx_len=%d", esp_err_to_name(ret), n); ESP_LOG_BUFFER_HEX_LEVEL(TAG, (const uint8_t *)line, n, ESP_LOG_WARN); } } return ret; }