首页 > 解决方案 > 使用 ESP-IDF 从 node.js 服务器发送图像到 ESP32

问题描述

我正在尝试使用 WIFI 和 ESP-IDF 将图像从 node.js 服务器发送到 ESP32 芯片。我相信这种传输是通过 TCP/IP 连接发生的。我可以使用 http get 请求或如下所示的 http_perform_as_stream_reader 函数发送常规文本数据。但是当谈到从 node.js 服务器传输图像时,我无法这样做。我似乎在客户端收到垃圾。最终目标是将图像保存到 ESP32 上的 spiffs 文件中。这是我的代码:

服务器端:

const {imageprocess, convertToBase64} = require('../canvas');
const fs = require('fs');
const express = require('express');
const router = express.Router();
const path = require('path');

const imageFile = path.resolve(__dirname, "../images/file31.png")

router.get('/', funcAllStream)

module.exports = router;


function funcAllStream(req, res, next){
        newStream(res, imageFile)
}

function newStream(res, imageFile){
  var readStream = fs.createReadStream(imageFile);
  readStream.on('data', chunk => {
    res.send(chunk.toString('hex'));
  })
}

客户端(C++):

static void http_perform_as_stream_reader(void)
{
    char *buffer = malloc(MAX_HTTP_RECV_BUFFER + 1);
    esp_http_client_config_t config = {
        .url = "http://192.168.1.155:8085/api?file=image1.png"
    };
    esp_http_client_handle_t client = esp_http_client_init(&config); //calls esp_http_client_init from standard ESP32 component esp_http_client
    esp_err_t err;
    if ((err = esp_http_client_open(client, 0)) != ESP_OK) {
        return;
    }
    int content_length =  esp_http_client_fetch_headers(client); //calls esp_http_client_fetch_headers from standard ESP32 component esp_http_client
    int total_read_len = 0, read_len;
    if (total_read_len < content_length && content_length <= MAX_HTTP_RECV_BUFFER) {//MAX_HTTP_RECV_BUFFER defined to be larger than image1.png file on server side
        read_len = esp_http_client_read(client, buffer, content_length); //calls esp_http_client_read from standard ESP32 component esp_http_client
        if (read_len <= 0) {
            ESP_LOGE(TAG, "Error read data");
        }
        buffer[read_len] = 0;
    }
    esp_http_client_close(client);
    esp_http_client_cleanup(client);
    free(buffer);
}

http_perform_as_stream_reader();

在客户端,我确保缓冲区分配的空间大于图像文件的大小。当我打印出客户端缓冲区中存储的内容时,我看到了绝对垃圾。服务器端发送如下所示的缓冲流:

<Buffer 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 00 64 00 00 00 64 08 06 00 00 00 70 e2 95 54 00 00 00 06 62 4b 47 44 00 ff 00 ff 00 ff a0 bd a7 ...>

这是客户端缓冲区在接收数据之前的样子:

���?Lv�~sN\
L��8J��s-�z���a�{�;�����\���~�Y���Di�2���]��|^,�x��N�݁�����`2g����n�w��b�Y�^���a���&amp;��wtD�&gt;n@�PQT�(�.z��(9,�?İ�

这是客户端缓冲区收到数据后的样子:

HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/octet-stream
Content-Length: 4517
ETag: W/"11a5-IhqwFPYLgC+NRfikTwS2exLtCWQ"
Date: Mon, 19 Apr 2021 16:15:48 GMT
Connection: keep-alive

�PNG

那么如何确保客户端缓冲区实际上从服务器端接收到正确格式的正确数据呢?最终,我想将数据存储在客户端的 spiffs 文件中并打开该文件以显示从 node.js 服务器传输的图像。

更新:在服务器端将数据转换为十六进制格式后,我可以确认客户端接收到正确的十六进制字符串。这是我在服务器端和客户端看到的:

89504e470d0a1a0a0000000d4948445200000064000000 ... 0fcc3c0ff03b8c85cc0643044ae0000000049454e44ae426082

开始和结束签名与 png 文件的签名一致。

I've set the client side buffer at 10000 (content length is 9037 bytes). Still, I receive the hex string in two chunks. In my client side code, function http_perform_as_stream_reader calls esp_http_client_fetch_headers from the ESP component esp_http_client, which in turn calls the lwip_rec_tcp function from lwip/src/api/sockets.c. Since I've set the buffer capacity to 10000 (a rather large amount), esp_http_client_fetch_headers fetches a rather large chunk of the hex string along with the headers. Then when the http_perform_as_stream_reader function calls the esp_http_client_read function, it again calls lwip_rec_tcp which now runs a do ... while loop until it retrieves the remaining server side data. Since lwip_rec_tcp stores all the data in the buffer, which has low storage capacity (certainly not up to the 10000 that I set for it in the code), the first chunk gets overwritten by the final chunk of data. So how do I ensure that the client->response->buffer pointer captures all the chunks of data without modifying lwip_rec_tcp to include uploading data to the spiffs file inside the do ... while loop?

标签: node.jscexpressbufferesp32

解决方案


Application should not assume that esp_http_client_read reads same number of bytes specified in the length argument. Instead, application should check the return value of this API which indicates number of bytes read by corresponding call. If the return value is zero, it indicates that complete data is read.

To work correctly, application should call esp_http_client_read in a while loop with a check for return value of esp_http_client_read.

You can also use esp_http_client_read_response, which is a helper API for esp_http_client_read to internally handle the while loop and read complete data in one go.

Please refer http_native_request example which uses esp_http_client_read_response() API.


推荐阅读