【问题标题】:Sensor value through UART in ESP32 correction通过 UART 在 ESP32 中校正传感器值
【发布时间】:2020-11-28 14:58:47
【问题描述】:

我正在使用 ESP-IDF SDK 开发一个小项目,通过 UART 获取传感器数据。我按照制造商提供的数据表来解析和计算不同参数的值。但是串行的输出不正确,每次我得到不同的输出都是错误的。 代码:-

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"
#include "driver/uart.h"
#include "string.h"
#include "driver/gpio.h"

static const int RX_BUF_SIZE = 1024;

#define TXD_PIN (GPIO_NUM_4)
#define RXD_PIN (GPIO_NUM_5)
#define DELAY_IN_MS(t) (((portTickType)t*configTICK_RATE_HZ)/(portTickType)1000)

void init(void) {
    const uart_config_t uart_config = {
        .baud_rate = 4800,
        .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_APB,
    };
    uart_driver_install(UART_NUM_1, RX_BUF_SIZE * 2, 129, 0, NULL, 0);
    uart_param_config(UART_NUM_1, &uart_config);
    uart_set_pin(UART_NUM_1, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
}


static void rx_task(void *arg)
{
    static const char *RX_TASK_TAG = "RX_TASK";
    esp_log_level_set(RX_TASK_TAG, ESP_LOG_INFO);
    uint8_t data[7] = {0};
    uint8_t PR=0,spo2=0,temprature=0;
    while (1) {
        const int rxBytes = uart_read_bytes(UART_NUM_1, data, 7, 1);
        if (rxBytes == 7) {
            printf("The rxbytes %d and %s\n",rxBytes,data);
            PR = (data[3] & 0x7F) + ((data[2] & 0x40)<<1);
            spo2 = (data[4] & 0x7F);
            temprature = (data[5] & 0x7F);
            printf("PR is %d , Spo2 is %d , temperature is %d \n",PR,spo2,temprature);
            ESP_LOGI(RX_TASK_TAG, "Read %d bytes: '%s'", rxBytes, data);
            ESP_LOG_BUFFER_HEXDUMP(RX_TASK_TAG, data, rxBytes, ESP_LOG_INFO);
            memset(data,0,7);
        }
    }
}

void app_main(void)
{
    init();
    xTaskCreate(rx_task, "uart_rx_task", 1024*2, NULL, configMAX_PRIORITIES, NULL);
}

程序在串行监视器上的输出是:-

制造商提供的数据表是:- https://drive.google.com/file/d/1lPATxeXXreVZkg9Ufg9BnyCrl4EsbJAj/view?usp=sharing

如果我在计算值时未对齐任何数据格式,请纠正我。

【问题讨论】:

    标签: c embedded uart esp32 esp-idf


    【解决方案1】:

    从不可靠的渠道(串行)组装消息意味着您不能真正依赖它们总是按您期望的顺序到达而没有任何问题,因此您必须采取预防措施以免收到垃圾邮件。

    代码假定它总是以 7 字节块的形式接收这些 7 字节消息,但它并不总是以这种方式工作。线路噪音或超时可能会导致以多个块(例如,4 字节然后 3 字节)接收正确的消息,或者可能导致字节丢失。

    要查看这是否是问题的一部分,请在每次读取时添加日志记录,而不仅仅是在您期望的读取时:

    static void rx_task(void *arg)
    {
        ...
        while (1) {
            const int rxBytes = uart_read_bytes(UART_NUM_1, data, 7, 1);
    
            // Log ALL reads, not just the ones you expect
            ESP_LOGI(RX_TASK_TAG, "Read %d bytes: '%s'", rxBytes, data);
            ESP_LOG_BUFFER_HEXDUMP(RX_TASK_TAG, data, rxBytes, ESP_LOG_INFO);
    
            if (rxBytes == 7) {
                    ///
            }
        }
    }
    

    这可能会证实我的预感。

    在任何情况下,您都不能依赖固定大小的消息,因为如果它不同步一次,它将永远无法恢复。这意味着您必须建立自己的保护措施。

    阅读传感器的数据表,它说每个 7 字节消息的 第一个 字节都设置了高位,所以这非常适合重新同步:你忽略所有内容,直到你得到那个开始字节,然后再读取 6 个字节,你就有了完整的消息。

    所以您最终需要 两个 缓冲区:一个用于您正在组装的消息,另一个用于从传感器执行原始 I/O,在您验证同步时复制到真正的消息缓冲区.

    快速而简单的方法如下所示:

    static void rx_task(void *arg)
    {
        static const char *RX_TASK_TAG = "RX_TASK";
        esp_log_level_set(RX_TASK_TAG, ESP_LOG_INFO);
    
        // sensor message we're trying to build
        uint8_t message[7] = {0};
        uint8_t *msgnext = message;
    
        while (1) {
            uint8_t inbuf[7];
    
            const int rxBytes = uart_read_bytes(UART_NUM_1, inbuf, sizeof inbuf, 1);
    
            ESP_LOGI(RX_TASK_TAG, "Read %d bytes: '%s'", rxBytes, inbuf);
            ESP_LOG_BUFFER_HEXDUMP(RX_TASK_TAG, inbuf, rxBytes, ESP_LOG_INFO);
    
            // error/timeout? do something?
            if (rxBytes <= 0) continue;
    
            for (int i = 0; i < rxBytes; i++)
            {
                const uint8_t b = inbuf[i];
    
                if (b & 0x80)
                {
                    // First byte of a message, reset the buffer
                     msgnext = message;
                    *msgnext++ = b;
                }   
    
                else if (msgnext == message)
                {
                    // not synced yet, ignore this byte
                    continue;
                }
                else
                {
                    *msgnext++ = b;
    
                    if ((msgnext - message) == sizeof message)
                    {
                        // WE FOUND A FULL MESSAGE
                        uint8_t PR   = (message[3] & 0x7F) + ((message[2] & 0x40)<<1);
                        uint8_t spo2 = (message[4] & 0x7F);
                        uint8_t temperature = (message[5] & 0x7F);
                        printf("PR is %d, Spo2 is %d, temperature is %d\n",
                             PR,spo2,temperature);
    
                        msgnext = message; // reset to empty the buffer
                    }
                }
            }
        }
    }
    

    这个想法是你的原始 I/O 完成到 inbuf,它首先寻找同步字节(设置高位),然后告诉你开始将数据复制到真正的传感器缓冲区 @987654326 @。获得 7 个字节后,它会显示结果并重置缓冲区。

    即使你有几个字节的有效message 数据,如果 另一个 SYNC 字节进来,它会假定之前的消息被搞砸了,所以它把它扔掉并开始一个新的新鲜缓冲液。

    您可以在此处添加更多内容,例如对超时的支持,或在部分消息被丢弃时检测/记录,但在任何情况下都不能避免此数据框架层。

    此外,I/O 缓冲区inbuf 不必与消息大小相同,从 UART 读取一个字节的块可能是有意义的;在多任务操作系统中我可能不会这样做,但在 ESP 环境中它可能有意义 - 不知道。这将简化一些循环。

    编辑查看您的实际数据转储,很明显您的消息没有正确构建,因为即使您有 7 个字节,SYNC 字节(设置了高位)也在中间,但每次都不在同一个地方。显然这是一个框架问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-12-24
      • 2021-05-01
      • 2011-11-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-03
      • 2013-07-14
      相关资源
      最近更新 更多