这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【瑞萨RA8D1LVGL/LWIP评测】网络环境监测站

共1条 1/1 1 跳转至

【瑞萨RA8D1LVGL/LWIP评测】网络环境监测站

高工
2026-02-07 20:54:02   被打赏 50 分(兑奖)     打赏

【目标实现】

瑞萨RA8D1LVGL/LWIP开发板获取家里的环境信息,实现lvgl、网络监测。

【瑞萨RA8D1LVGL/LWIP评测】基于STCC4的环境监测系统-电子产品世界论坛

在这一篇里,我实现了lvgl的监测。这一篇在其监测的基础之上添加lwip,并添加tcp服务器。

【硬件】

1、CPKCOR-RA8D1B核心板

2、拓展板(CPKEXP-EKRA8X1)

3、STCC4二氧化碳环境监测模组

4、路由器 & 网线

【硬件连接】

核心板上的RA8D1已经集成了以太网 MAC 控制器,完整的底层硬件链路还需要以太网PHY、隔离变压器、RJ45接口。CPKEXP-EKRA8x1 扩展板提供了这部分外部硬件,我们只需要连接好相应的跳线帽即可,如图:

image.png

【网络配置】

1、我们需要导入CPKEXP-EKRA8X1的PIN的配置,可以从CPKEXP-EKRA8X1的例程中导入。image.png


2、新建一个LWIP的freertos任务

image.png

在这个任务中添加Lwip system FreeRTOS port,一个Lwip HTTP Server。

3、打开Enable Backward Compatibility【注:这个一定要打开,要不在lwip tcpip 一直会报错】

image.png

4、把freertos 的Total Heap Size修改为81920【注:太小了会出现某些任务启动不了】

image.png

5、Tread配置如下:

image.png

6、在tcp_ip的配置中Mem size修改为40960

image.png

7、Internal memory pools配置参数如下:

image.png

8、Threading配置如下:

image.png

9、NETIF中打开LwIP netif status callback、LwIP netif link callback、LwIP netif remove callback 、LwIP num netif client data。

image.png

10、打开LwIP support custom pbuf:

image.png

11、添加网络并配置如下:

image.png

最后配置网络如下图所示:

image.png

【代码实现】

1、添加tcp_echo_server_thread.c。

#include <lwip_thread.h>
#include "common_utils.h"
#include "lwip/init.h"
#include "lwip/tcpip.h"
#include "lwip/tcp.h"
#include "lwip/timeouts.h"
#include "lwip/init.h"
#include "lwip/ip4.h"
#include <lwip/sockets.h>
#include <stdio.h> // For snprintf, sscanf
#include <string.h> // For strstr, strncmp, strlen

#define LOCAL_PORT 80 // Changed to standard HTTP port
#define RECV_BUF_SIZE (512) // Increased buffer size to accommodate the full HTML page
#define SEND_BUF_SIZE (4096) // Size for temporary buffers like headers or small responses

// --- External Global Variables for Sensor Data ---
extern uint16_t co2_concentration_raw;
extern uint8_t sensor_status_raw;
extern float hum, temp;
// --- End External Variables ---

// The HTML content from sensor_data.html embedded as a C string
const char* const HTML_PAGE =
"<!DOCTYPE html>\n"
"<html>\n"
"<head>\n"
" <title>Environmental Monitoring Station</title>\n"
" <style>\n"
" body { font-family: Arial, sans-serif; margin: 40px; background-color: #f0f0f0; }\n"
" h1 { color: #333; }\n"
" table { border-collapse: collapse; width: 50%; margin-top: 20px; }\n"
" th, td { border: 1px solid #ddd; padding: 12px; text-align: left; }\n"
" th { background-color: #4CAF50; color: white; }\n"
" tr:nth-child(even) { background-color: #f2f2f2; }\n"
" input[type=text] { padding: 10px; font-size: 16px; }\n"
" button { padding: 10px 20px; font-size: 16px; cursor: pointer; }\n"
" #statusMessage { margin-top: 20px; color: #d32f2f; font-weight: bold; }\n"
" </style>\n"
"</head>\n"
"<body>\n"
"\n"
" <h1>Environmental Monitoring Station</h1>\n"
" <label for=\"arduinoIP\">Enter Arduino IP Address:</label>\n"
" <input type=\"text\" id=\"arduinoIP\" placeholder=\"e.g., 192.168.3.12\">\n"
" <button onclick=\"connectToArduino()\">Connect</button>\n"
"\n"
" <table id='dataTable' border='1'>\n"
" <tr><th>Parameter</th><th>Value</th></tr>\n"
" <tr><td colspan=\"2\">Please enter the Arduino IP address and click Connect.</td></tr>\n"
" </table>\n"
" <div id='statusMessage'>Status: Waiting for connection...</div>\n"
"\n"
" <script>\n"
" let pollingIntervalId = null;\n"
"\n"
" function connectToArduino() {\n"
" const ip = document.getElementById(\"arduinoIP\").value.trim();\n"
" if (!ip) {\n"
" document.getElementById('statusMessage').textContent = 'Status: Please enter an IP address.';\n"
" return;\n"
" }\n"
"\n"
" document.getElementById('statusMessage').textContent = `Status: Connecting to ${ip}...`;\n"
"\n"
" // Stop any previous polling\n"
" if (pollingIntervalId) {\n"
" clearInterval(pollingIntervalId);\n"
" pollingIntervalId = null;\n"
" }\n"
"\n"
" // Attempt to fetch data once to check connection\n"
" fetchDataFromIP(ip).then(success => {\n"
" if (success) {\n"
" // If initial fetch succeeds, start polling every 5 seconds\n"
" pollingIntervalId = setInterval(() => fetchDataFromIP(ip), 5000);\n"
" }\n"
" }).catch(() => {\n"
" // Error handling already done in fetchDataFromIP\n"
" });\n"
" }\n"
"\n"
" // Modified function to fetch from the /data endpoint\n"
" function fetchDataFromIP(ip) {\n"
" return fetch(`http://${ip}/data`) // <--- Changed from / to /data\n"
" .then(response => {\n"
" if (!response.ok) {\n"
" throw new Error(`HTTP error! Status: ${response.status}`);\n"
" }\n"
" return response.json(); // Expecting JSON from /data\n"
" })\n"
" .then(data => {\n"
" updateTable(data);\n"
" document.getElementById('statusMessage').textContent = `Status: Connected to ${ip}. Data updated.`;\n"
" return true; // Indicate success\n"
" })\n"
" .catch(error => {\n"
" console.error('Error fetching data:', error);\n"
" document.getElementById('statusMessage').textContent = `Status: Error fetching data from ${ip} - ${error.message}`;\n"
" // Optionally clear data on error\n"
" const table = document.getElementById('dataTable');\n"
" table.innerHTML = '<tr><th>Parameter</th><th>Value</th></tr><tr><td colspan=\"2\">Error loading data</td></tr>';\n"
" return false; // Indicate failure\n"
" });\n"
" }\n"
"\n"
" function updateTable(data) {\n"
" const table = document.getElementById('dataTable');\n"
" table.innerHTML = '<tr><th>Parameter</th><th>Value</th></tr>'; // Clear existing rows except header\n"
" \n"
" // Add rows for each data item\n"
" for (const [key, value] of Object.entries(data)) {\n"
" const row = table.insertRow();\n"
" const cell1 = row.insertCell(0);\n"
" const cell2 = row.insertCell(1);\n"
" cell1.innerText = key.charAt(0).toUpperCase() + key.slice(1); // Capitalize first letter\n"
" cell2.innerText = value;\n"
" }\n"
" }\n"
" </script>\n"
"\n"
"</body>\n"
"</html>\n";

/**
 * @brief Helper function to ensure all data is written to the socket.
 * This is crucial because a single 'write' call might not send all data.
 * @param sock: The socket file descriptor.
 * @param buf: Pointer to the data buffer to send.
 * @param count: Number of bytes to send.
 * @return: Total number of bytes sent on success, -1 on error.
 */
static ssize_t write_all(int sock, const void *buf, size_t count) {
    const char *ptr = (const char*)buf;
    size_t sent = 0;
    ssize_t res;

    while (sent < count) {
        res = write(sock, ptr + sent, count - sent);
        if (res < 0) {
            // An error occurred during write
            APP_PRINT("Error writing to socket: %d\n", res);
            return -1; // Propagate error
        }
        sent += (size_t)res; // Add the number of bytes successfully sent
    }
    // All data was successfully sent
    return sent;
}

/**
 * @brief Thread function for the TCP Server.
 * Serves the HTML page on '/' and JSON data on '/data'.
 * @param arg: Not used
 */
static void tcpecho_thread(void *arg) {
    int sock = -1, connected = -1;
    char *recv_buf;
    char *send_buf; // Temporary buffer for headers and small responses
    struct sockaddr_in server_addr, client_addr;
    socklen_t sin_size;
    size_t recv_data_len;
    int flag = 1;
    (void)arg;

    APP_PRINT("HTTP Server starting on Local port: %d\n", LOCAL_PORT);

    recv_buf = (char *)pvPortMalloc(RECV_BUF_SIZE);
    send_buf = (char *)pvPortMalloc(SEND_BUF_SIZE);

    if (!recv_buf || !send_buf) {
        APP_PRINT("Error: Failed to allocate memory for buffers.\n");
        goto __exit;
    }

    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
        APP_PRINT("Error: Socket creation failed.\n");
        goto __exit;
    }

    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(LOCAL_PORT);
    memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));

    if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) {
        APP_PRINT("Error: Unable to bind socket.\n");
        goto __exit;
    }

    if (listen(sock, 5) == -1) {
        APP_PRINT("Error: Listen failed.\n");
        goto __exit;
    }

    APP_PRINT("Server listening for HTTP connections...\n");

    while(1) {
        sin_size = sizeof(struct sockaddr_in);
        connected = accept(sock, (struct sockaddr *)&client_addr, &sin_size);
        if (connected < 0) {
            APP_PRINT("Error: Accept failed.\n");
            continue;
        }
        APP_PRINT("New HTTP client connected from (%s, %d)\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

        setsockopt(connected, IPPROTO_TCP, TCP_NODELAY, (void *) &flag, sizeof(int));

        // --- Handle Client Request ---
        memset(recv_buf, 0, RECV_BUF_SIZE);
        recv_data_len = recv(connected, recv_buf, RECV_BUF_SIZE - 1, 0);
        if (recv_data_len > 0) {
            recv_buf[recv_data_len] = '\0';
            APP_PRINT("Received request:\n%s\n", recv_buf); // Print full request for debugging

            // --- NEW PARSING LOGIC USING SSCANF ---
            char method[8];  // GET, POST, etc.
            char path[256];  // URL path like /, /data, etc.
            char http_version[16]; // HTTP/1.0, HTTP/1.1, etc.

            // Parse the request line: "METHOD PATH HTTP_VERSION"
            // %s reads up to the first whitespace character.
            int parsed_items = sscanf(recv_buf, "%7s %255s %15s", method, path, http_version);
            if (parsed_items == 3) {
                APP_PRINT("Parsed Method: %s, Path: %s, Version: %s\n", method, path, http_version);

                if (strncmp(method, "GET", 3) == 0) {
                    if (strcmp(path, "/") == 0) { // Serve the main HTML page
                        APP_PRINT("Serving main HTML page.\n");
                        // Calculate Content-Length for the HTML body
                        size_t html_len = strlen(HTML_PAGE);
                        // Prepare the response header
                        int header_len = snprintf(send_buf, SEND_BUF_SIZE,
                                                  "HTTP/1.1 200 OK\r\n"
                                                  "Content-Type: text/html\r\n"
                                                  "Access-Control-Allow-Origin: *\r\n"
                                                  "Access-Control-Allow-Methods: GET\r\n"
                                                  "Access-Control-Allow-Headers: Content-Type\r\n"
                                                  "Content-Length: %zu\r\n"
                                                  "Connection: close\r\n"
                                                  "\r\n", // Header ends with an extra \r\n
                                                  html_len);

                        // Send the header first
                        if (write_all(connected, send_buf, header_len) < 0) {
                            APP_PRINT("Error sending response header.\n");
                            goto cleanup_and_continue;
                        }

                        // Send the HTML page content using the helper function
                        if (write_all(connected, HTML_PAGE, html_len) < 0) {
                             APP_PRINT("Error sending HTML page content.\n");
                             // The connection will be closed below anyway
                        } else {
                            APP_PRINT("Sent HTML response (%zu bytes body) to client.\n", html_len);
                        }
                        // Done sending HTML page, go to cleanup
                        goto cleanup_and_continue;

                    } else if (strcmp(path, "/data") == 0) { // Serve the JSON data - CORRECTED LOGIC
                        APP_PRINT("Serving JSON data.\n");
                        // Step 1: Calculate the length of the JSON body
                        int json_body_len = snprintf(NULL, 0, // Calculate length only
                                                     "{ \"co2\": %u, \"temperature\": %.1f, \"humidity\": %.1f, \"status\": \"0x%02X\" }",
                                                     co2_concentration_raw, (double)temp, (double)hum, sensor_status_raw);
                        if (json_body_len <= 0) {
                            APP_PRINT("Error: Failed to calculate JSON body length.\n");
                            snprintf(send_buf, SEND_BUF_SIZE, "HTTP/1.1 500 Internal Server Error\r\nConnection: close\r\n\r\n");
                        } else {
                            // Step 2: Format the response header
                            int header_len = snprintf(send_buf, SEND_BUF_SIZE,
                                                      "HTTP/1.1 200 OK\r\n"
                                                      "Content-Type: application/json\r\n"
                                                      "Access-Control-Allow-Origin: *\r\n"
                                                      "Access-Control-Allow-Methods: GET\r\n"
                                                      "Access-Control-Allow-Headers: Content-Type\r\n"
                                                      "Content-Length: %d\r\n"
                                                      "Connection: close\r\n"
                                                      "\r\n", // Header ends with an extra \r\n
                                                      json_body_len);

                            // Step 3: Send the header first
                            if (write_all(connected, send_buf, header_len) < 0) {
                                APP_PRINT("Error sending JSON response header.\n");
                                goto cleanup_and_continue;
                            }

                            // Step 4: Send the JSON body
                            int body_len = snprintf(send_buf, SEND_BUF_SIZE, // Use send_buf to hold the JSON body
                                                    "{ \"co2\": %u, \"temperature\": %.1f, \"humidity\": %.1f, \"status\": \"0x%02X\" }",
                                                    co2_concentration_raw, (double)temp, (double)hum, sensor_status_raw);
                            if (body_len < 0 || body_len != json_body_len) {
                                 APP_PRINT("Error: Formatting JSON body failed or mismatched length.\n");
                                 // Connection will be closed below
                                 goto cleanup_and_continue;
                            }

                            if (write_all(connected, send_buf, body_len) < 0) {
                                APP_PRINT("Error sending JSON response body.\n");
                            } else {
                                APP_PRINT("Sent JSON response (%d bytes body) to client.\n", body_len);
                            }
                            // Done sending JSON data, go to cleanup
                            goto cleanup_and_continue;
                        }
                    } else { // 404 for other paths
                        APP_PRINT("Path '%s' not found, sending 404.\n", path);
                        snprintf(send_buf, SEND_BUF_SIZE,
                                 "HTTP/1.1 404 Not Found\r\n"
                                 "Content-Type: text/plain\r\n"
                                 "Access-Control-Allow-Origin: *\r\n"
                                 "Access-Control-Allow-Methods: GET\r\n"
                                 "Access-Control-Allow-Headers: Content-Type\r\n"
                                 "Content-Length: 9\r\n"
                                 "Connection: close\r\n"
                                 "\r\n"
                                 "Not Found");
                    }
                } else { // Only handle GET requests
                    APP_PRINT("Unsupported method '%s', sending 405.\n", method);
                    snprintf(send_buf, SEND_BUF_SIZE,
                             "HTTP/1.1 405 Method Not Allowed\r\n"
                             "Content-Type: text/plain\r\n"
                             "Access-Control-Allow-Origin: *\r\n"
                             "Access-Control-Allow-Methods: GET\r\n"
                             "Access-Control-Allow-Headers: Content-Type\r\n"
                             "Content-Length: 15\r\n"
                             "Connection: close\r\n"
                             "\r\n"
                             "Method Not Allowed");
                }
            } else { // Malformed request line
                APP_PRINT("Malformed request line (parsed %d items), sending 400.\n", parsed_items);
                snprintf(send_buf, SEND_BUF_SIZE,
                         "HTTP/1.1 400 Bad Request\r\n"
                         "Content-Type: text/plain\r\n"
                         "Access-Control-Allow-Origin: *\r\n"
                         "Access-Control-Allow-Methods: GET\r\n"
                         "Access-Control-Allow-Headers: Content-Type\r\n"
                         "Content-Length: 11\r\n"
                         "Connection: close\r\n"
                         "\r\n"
                         "Bad Request");
            }
            // --- END OF NEW PARSING LOGIC ---

            // Send the response stored in send_buf (for 404, 405, 500, or malformed requests)
            int send_result = write_all(connected, send_buf, strlen(send_buf));
            if (send_result < 0) {
                APP_PRINT("Error sending data to client.\n");
            } else {
                APP_PRINT("Sent HTTP response (%d bytes) to client.\n", send_result);
            }

        } else {
            APP_PRINT("No data received from client.\n");
        }

cleanup_and_continue:
        if (connected >= 0) {
            closesocket(connected);
            APP_PRINT("Client connection closed.\n");
        }
        connected = -1;
    }

__exit:
    if (sock >= 0) closesocket(sock);
    if (recv_buf) vPortFree(recv_buf); // Use FreeRTOS functions if applicable
    if (send_buf) vPortFree(send_buf);
    APP_PRINT("HTTP Server thread exiting.\n");
}

/*-----------------------------------------------------------------------------------*/
void tcp_echo_server_init(void) {
    sys_thread_new("tcpecho_thread", tcpecho_thread, NULL, 2048, 4);
}

代码的思路是创建tcp_server并监听,当有tcpclient进来时,向其发送请求的数据。

2、在lwip_tread_entry.c中添加lwip初始化以启动tcp_server,代码如下:

#include <lwip_thread.h>
#include "common_utils.h"
#include "usr_app.h"

#include "lwip/init.h"
#include "lwip/tcpip.h"
#include "lwip/tcp.h"
#include "lwip/timeouts.h"
#include "lwip/init.h"
#include "lwip/ip4.h"
#include "lwip/dhcp.h"
#include <lwip/sockets.h>

struct netif gnetif;
ip4_addr_t ipaddr;
ip4_addr_t netmask;
ip4_addr_t gw;

void tcpecho_init(void);
extern void tcp_echo_server_init(void);
/*---------------------------------------------------------------------------*/
/* 1) Define your callback functions                                         */
/*---------------------------------------------------------------------------*/
void ethernet_link_status_updated(struct netif *netif)
{
    if (netif_is_link_up(netif)) {
        APP_PRINT("LINK UP\n\n");
    } else {
        APP_PRINT("LINK DOWN\n\n");
    }
}

void LwIP_Init(void)
{
    LWIP_DEBUGF(TCP_DEBUG, ("RTT Debug Enabled\n"));
    /* Initialize the LwIP stack with RTOS */
    tcpip_init(NULL, NULL);

//    ipaddr.addr = 0;
//    netmask.addr = 0;
//    gw.addr = 0;
    IP4_ADDR(&ipaddr, 192, 168, 3, 12);
    IP4_ADDR(&gw, 192, 168, 3, 1);
    IP4_ADDR(&netmask, 255, 255, 255, 0);

    /* add the network interface (IPv4/IPv6) with RTOS */
    netif_add(&gnetif, &ipaddr, &netmask, &gw, &g_lwip_ether0_instance, &rm_lwip_ether_init, &tcpip_input);
    /* Registers the default network interface */
    netif_set_default(&gnetif);
    /* When the netif is fully configured this function must be called */
    netif_set_up(&gnetif);
    netif_set_link_up(&gnetif);

    /* Set the link callback function, this function is called on change of link status*/
    netif_set_link_callback(&gnetif, ethernet_link_status_updated);


    APP_PRINT("Local IP :%d.%d.%d.%d\n\n",  \
               ((gnetif.ip_addr.addr)&0x000000ff),       \
               (((gnetif.ip_addr.addr)&0x0000ff00)>>8),  \
               (((gnetif.ip_addr.addr)&0x00ff0000)>>16), \
               ((gnetif.ip_addr.addr)&0xff000000)>>24);
    APP_PRINT("Local Netmask :%d.%d.%d.%d\n\n",  \
               ((gnetif.netmask.addr)&0x000000ff),       \
               (((gnetif.netmask.addr)&0x0000ff00)>>8),  \
               (((gnetif.netmask.addr)&0x00ff0000)>>16), \
               ((gnetif.netmask.addr)&0xff000000)>>24);
    APP_PRINT("Local GW :%d.%d.%d.%d\n\n",  \
               ((gnetif.gw.addr)&0x000000ff),       \
               (((gnetif.gw.addr)&0x0000ff00)>>8),  \
               (((gnetif.gw.addr)&0x00ff0000)>>16), \
               ((gnetif.gw.addr)&0xff000000)>>24);
    ip_addr_set(&ipaddr,&(gnetif.ip_addr));
    ip_addr_set(&netmask,&(gnetif.netmask));
    ip_addr_set(&gw,&(gnetif.gw));
}


/*******************************************************************************************************************//**
* @brief      This is the User Thread for the EP.
* @param[in]  Thread specific parameters
* @retval     None
**********************************************************************************************************************/
void lwip_thread_entry(void *pvParameters)
{
    fsp_pack_version_t version = {RESET_VALUE};
    FSP_PARAMETER_NOT_USED(pvParameters);

    /* version get API for FLEX pack information */
    R_FSP_VersionGet (&version);
    /* Example Project information printed on the RTT */
    APP_PRINT (BANNER_INFO, EP_VERSION, version.version_id_b.major, version.version_id_b.minor, version.version_id_b.patch);

    LwIP_Init();
    tcp_echo_server_init();

    /* If this thread is done, you can delete it */
    vTaskDelete(NULL);
}

在这个工程中,我们配置开发板的ip与有我们的所在的网络的网段一致。

【测试】

编译下载后,我们ping一下开发板:

image.png

顺利的连上了网络。

编写测试网页:

<!DOCTYPE html>
<html>

<head>
    <title>RA8D1 环境监测站</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 40px;
            background-color: #f0f0f0;
        }

        h1 {
            color: #333;
        }

        table {
            border-collapse: collapse;
            width: 50%;
            margin-top: 20px;
        }

        th,
        td {
            border: 1px solid #ddd;
            padding: 12px;
            text-align: left;
        }

        th {
            background-color: #4CAF50;
            color: white;
        }

        tr:nth-child(even) {
            background-color: #f2f2f2;
        }

        input[type=text] {
            padding: 10px;
            font-size: 16px;
        }

        button {
            padding: 10px 20px;
            font-size: 16px;
            cursor: pointer;
        }

        #statusMessage {
            margin-top: 20px;
            color: #d32f2f;
            font-weight: bold;
        }
    </style>
</head>

<body>

    <h1>RA8D1 环境监测站</h1>
    <label for="arduinoIP">请输入 RA8D1 开发板的 IP 地址:</label>
    <input type="text" id="arduinoIP" placeholder="例如: 192.168.3.12">
    <button onclick="connectToArduino()">连接</button>

    <table id='dataTable' border='1'>
        <tr>
            <th>参数</th>
            <th>数值</th>
        </tr>
        <tr>
            <td colspan="2">请输入 RA8D1 开发板的 IP 地址并点击连接。</td>
        </tr>
    </table>
    <div id='statusMessage'>状态: 等待连接...</div>

    <script>
        let pollingIntervalId = null;

        function connectToArduino() {
            const ip = document.getElementById("arduinoIP").value.trim();
            if (!ip) {
                document.getElementById('statusMessage').textContent = '状态: 请输入一个 IP 地址.';
                return;
            }

            document.getElementById('statusMessage').textContent = `状态: 正在连接到 ${ip}...`;

            // 停止任何先前的轮询
            if (pollingIntervalId) {
                clearInterval(pollingIntervalId);
                pollingIntervalId = null;
            }

            // 尝试获取一次数据以检查连接
            fetchDataFromIP(ip).then(success => {
                if (success) {
                    // 如果初始获取成功,则每 5 秒轮询一次
                    pollingIntervalId = setInterval(() => fetchDataFromIP(ip), 5000);
                }
            }).catch(() => {
                // 错误处理已在 fetchDataFromIP 中完成
            });
        }

        // 修改后的函数,从 /data 端点获取数据
        function fetchDataFromIP(ip) {
            return fetch(`http://${ip}/data`) // <--- Changed from / to /data
                .then(response => {
                    if (!response.ok) {
                        throw new Error(`HTTP 错误! 状态: ${response.status}`);
                    }
                    return response.json(); // 期望从 /data 获取 JSON
                })
                .then(data => {
                    updateTable(data);
                    document.getElementById('statusMessage').textContent = `状态: 已连接到 ${ip}。数据已更新.`;
                    return true; // 表示成功
                })
                .catch(error => {
                    console.error('获取数据时出错:', error);
                    document.getElementById('statusMessage').textContent = `状态: 从 ${ip} 获取数据时出错 - ${error.message}`;
                    // 可选择在出错时清除数据
                    const table = document.getElementById('dataTable');
                    table.innerHTML = '<tr><th>参数</th><th>数值</th></tr><tr><td colspan="2">加载数据出错</td></tr>';
                    return false; // 表示失败
                });
        }

        function updateTable(data) {
            const table = document.getElementById('dataTable');
            table.innerHTML = '<tr><th>参数</th><th>数值</th></tr>'; // 清除现有行,保留表头

            // 为每个数据项添加行
            for (const [key, value] of Object.entries(data)) {
                const row = table.insertRow();
                const cell1 = row.insertCell(0);
                const cell2 = row.insertCell(1);
                // 将英文参数名转换为常见的中文名称 (可选)
                let chineseKey = key.charAt(0).toUpperCase() + key.slice(1); // 默认首字母大写
                if (key.toLowerCase() === 'co2') chineseKey = '二氧化碳浓度 (ppm)';
                if (key.toLowerCase() === 'temperature') chineseKey = '温度 (°C)';
                if (key.toLowerCase() === 'humidity') chineseKey = '湿度 (%)';
                if (key.toLowerCase() === 'status') chineseKey = '传感器状态';

                cell1.innerText = chineseKey;
                cell2.innerText = value;
            }
        }
    </script>

</body>

</html>

打开网页,输入IP地址后,在网页上就可以实现的监测到环境数据了。

image.png

08c424f61ad0983156f3642cce07eaff.png

【总结】

瑞萨的RASC图形化的配置工具网络非常方便工程的配置。提供的pin的配置,我们不需要自己去编写复杂的管脚配置,可以快速的实现lwip。同时RA8D1性能非常强大,在运行lvgl下面,也可以高效的处理网络请求。

【感谢】

非常感谢@枫雪天 大佬的指导,此次工程是学习他的freertos+lwip_tcp工程的基础上创建出来的。




关键词: RA8D1LVGL     瑞萨     网络     lwip     tcp    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]