{"id":25086750,"url":"https://github.com/electroluxcode/esp32cam-node-server","last_synced_at":"2025-10-07T23:19:00.840Z","repository":{"id":276186369,"uuid":"928502941","full_name":"electroluxcode/esp32cam-node-server","owner":"electroluxcode","description":"esp32cam node服务转发 \u0026 公网部署教程","archived":false,"fork":false,"pushed_at":"2025-02-06T18:40:53.000Z","size":3782,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-01T14:49:28.895Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/electroluxcode.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-02-06T18:35:44.000Z","updated_at":"2025-02-07T02:52:23.000Z","dependencies_parsed_at":null,"dependency_job_id":"3abb24fd-e1f0-484b-b49d-c4cb5876a3e9","html_url":"https://github.com/electroluxcode/esp32cam-node-server","commit_stats":null,"previous_names":["electroluxcode/esp32cam-node-server"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/electroluxcode/esp32cam-node-server","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/electroluxcode%2Fesp32cam-node-server","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/electroluxcode%2Fesp32cam-node-server/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/electroluxcode%2Fesp32cam-node-server/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/electroluxcode%2Fesp32cam-node-server/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/electroluxcode","download_url":"https://codeload.github.com/electroluxcode/esp32cam-node-server/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/electroluxcode%2Fesp32cam-node-server/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278861089,"owners_count":26058641,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-07T02:00:06.786Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-02-07T09:18:52.920Z","updated_at":"2025-10-07T23:19:00.834Z","avatar_url":"https://github.com/electroluxcode.png","language":"C","readme":"# esp32-cam-node\n\n\u003e 最近在学习硬件相关的，发现网上似乎没有用node做中转服务器的，简单记录一下esp32-cam通过node转发到公网的流程， 我的硬件是 OV2640_PID + esp32 cam，成本30左右\n\n\n\n\u003cimg src=\"./static/pic.png\"/\u003e\n\n\n\n\u003e 最终效果如下\n\n\n\n\u003cimg src=\"./static/record.gif\"/\u003e\n\n## esp32转发到node服务\n\n首先在esp32 中 搭建服务，然后把帧数据转发到 node的3008端口，这个端口充当数据处理的作用， 具体流程见下图\n\n```mermaid\ngraph TD\n    A[ESP32-CAM搭建服务] --\u003e B[TCP 3008端口]\n    B --\u003e|存储接收到的帧数据,并追加到缓冲区| E[WS 3009端口]\n   \n\n```\n\n\n### esp32-cam配置\n\n用的是arduino的CameraWebServer 示例代码然后做了点修改， 原来的代码见这里\n\nhttps://github.com/espressif/arduino-esp32/blob/master/libraries/ESP32/examples/Camera/CameraWebServer/\n\n核心修改的点如下 \n\n- 1. CameraWebServer.ino:  esp32启动文件\n\n  -  wifi账号和密码：ssid  和  password 换成你自己的 wifi账号和密码\n  -  摄像头型号：`if (s-\u003eid.PID == OV3660_PID)` 中的 ov后面的型号换成你自己的\n  - 板子型号：`#define CAMERA_MODEL_AI_THINKER `  的注释 打开，然后把那个默认的 CAMERA_MODEL 注释掉\n\n\n\n- 2. app_httpd.cpp\n\n主要是添加 发送帧的事件，下面是工具方法\n\n```c\n\n#include \u003clwip/sockets.h\u003e\n#define LED_LEDC_CHANNEL 2 //Using different ledc channel/timer than camera\n// 服务器配置\nconst char *server_ip = \"\"; // 替换为你的 Node.js 服务器 IP\nconst int server_port = 3008;           // 替换为你的 Node.js 服务器端口\n\nstruct UploadParams {\n    uint8_t *buf;\n    size_t len;\n};\n\n// 定义帧上传任务\nvoid uploadFrameToServer(uint8_t *frame, size_t frame_len) {\n    int sockfd;\n    struct sockaddr_in server_addr;\n    // 创建 Socket\n    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) \u003c 0) {\n        ESP_LOGE(\"SOCKET\", \"Socket creation error\");\n         Serial.print(\"Socket creation error\");\n        return;\n    }\n\n    // 配置服务器地址\n    server_addr.sin_family = AF_INET;\n    server_addr.sin_port = htons(server_port);\n    if (inet_pton(AF_INET, server_ip, \u0026server_addr.sin_addr) \u003c= 0) {\n        ESP_LOGE(\"SOCKET\", \"Invalid address / Address not supported\");\n        Serial.print(\"Invalid address / Address not supported\");\n        return;\n    }\n\n    // 连接到服务器\n    if (connect(sockfd, (struct sockaddr *)\u0026server_addr, sizeof(server_addr)) \u003c 0) {\n        ESP_LOGE(\"SOCKET\", \"Connection failed\");\n        Serial.print(\" connect error \");\n        return;\n    }\n\n    // 发送帧数据\n    if (send(sockfd, frame, frame_len, 0) \u003c 0) {\n        ESP_LOGE(\"SOCKET\", \"Frame send failed\");\n    } else {\n        ESP_LOGI(\"SOCKET\", \"Frame sent successfully\");\n    }\n\n    // 关闭 Socket\n    close(sockfd);\n}\n\n```\n\n\n\n然后在合适的地方调用它， 我们不难发现 代码中有很多 `if (res == ESP_OK) {` 结合上下文，这里是发送帧事件的地方\n\n\n\n```c\n// ================ 新增上传代码 ================\nif (res == ESP_OK \u0026\u0026 _jpg_buf != NULL \u0026\u0026 _jpg_buf_len \u003e 0) {\n    // 深拷贝帧数据\n    uint8_t *upload_buf = (uint8_t*)malloc(_jpg_buf_len);\n    if (upload_buf) {\n        memcpy(upload_buf, _jpg_buf, _jpg_buf_len);\n        \n        // 创建参数结构体\n        UploadParams *params = (UploadParams*)malloc(sizeof(UploadParams));\n        params-\u003ebuf = upload_buf;\n        params-\u003elen = _jpg_buf_len;\n        \n        // 创建异步上传任务\n        xTaskCreatePinnedToCore(\n            [](void *param) {\n                UploadParams *p = (UploadParams*)param;\n                uploadFrameToServer(p-\u003ebuf, p-\u003elen);\n                free(p-\u003ebuf); // 释放缓冲区\n                free(p);      // 释放参数结构体\n                vTaskDelete(NULL);\n            },\n            \"frame_upload\",\n            8192,       // 堆栈大小\n            params,     // 传递参数结构体\n            9999,          // 优先级（高于主循环）\n            NULL, \n            PRO_CPU_NUM // 运行在PRO核心\n        );\n    } else {\n        log_e(\"Malloc for upload buffer failed!\");\n    }\n    \n    delay(100);\n}\n```\n\n\n\n最后把代码烧录进去就好了。至此 esm32的配置就结束了\n\n\n\n### tcp-3008配置\n\n通过node的net模块创造一个tcp出来, 这部分代码比较简单，接收esm32发送的帧数据然后用buffer组装即可\n\n```js\n\nimport net from 'net';\nimport {WebSocketServer } from 'ws';\n\n\n// step1: 配置\n\n// 1.1 tcp服务器端口，用来接收c语言帧数据\nconst PORT = 3008;\nconst HOST = '0.0.0.0'; \n\n// 1.2 node 服务器端口, 用来连接c语言服务器和前端，起到转发的作用\nconst WS_PORT = 3009;\n\n// 1.3 前端端口\nconst FRONT_PORT = 3010;\n\n\n// step1: 创建 前端用以交互的 WebSocket 服务器\nconst webSocketServerCase = new WebSocketServer({  port: WS_PORT });\n// step2: 创建 TCP 服务器\nconst server = net.createServer((socket) =\u003e {\n    console.log('Client connected:', socket.remoteAddress, socket.remotePort,);\n\n    let frameBuffer = Buffer.alloc(0); // 用于存储接收到的帧数据\n\n    // 接收数据\n    socket.on('data', (data) =\u003e {\n        frameBuffer = Buffer.concat([frameBuffer, data]); // 将数据追加到缓冲区\n    });\n    // 客户端断开连接时保存帧数据\n    socket.on('end', () =\u003e {\n       \n    });\n\n    // 错误处理\n    socket.on('error', (err) =\u003e {\n        console.error('Socket error:', err);\n    });\n});\n\n```\n\n\n\n\n\n\n\n\n\n## node 服务中转帧数据\n\n```mermaid\ngraph TD\n    E[WS 3009端口] --\u003e|转发帧数据| G[前端 WebSocket 客户端]\n    G --\u003e|渲染数据| H[前端页面]\n```\n\n\n\n### ws-3009配置\n\n这部分核心代码如下\n\n```js\n// 发送\n...\n\nsocket.on('end', () =\u003e {\n        if (frameBuffer.length \u003e 0) {\n            // 将帧数据发送给前端\n            if (webSocketServerCase.clients.size \u003e 0) {\n                for (const client of webSocketServerCase.clients){\n                    client.send(frameBuffer);\n                }\n            }\n        }\n});\n\n...\n// 前端页面\nimport express from 'express';\nimport cors from 'cors';\nimport path from 'path';\nconst app = express();\n\napp.use(cors());\n// step4: 嵌入前端\napp.get('/',(req,res)=\u003e{\n    res.sendFile(path.resolve(\"./index.html\"))        //设置/ 下访问文件位置\n});\napp.listen(FRONT_PORT, () =\u003e {\n    console.log(`Server listening on ${HOST}:${FRONT_PORT}`);\n});\n```\n\n\n\n\n\n### html界面\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n    \u003ctitle\u003eSTM32 CAM Viewer\u003c/title\u003e\n    \u003cstyle\u003e\n        img { max-width: 80%; margin: 20px; border: 2px solid #333; }\n    \u003c/style\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n    \u003cimg id=\"frame\" src=\"\" alt=\"Live Frame\"\u003e\n    \u003cscript\u003e\n        const address = 'ws://your-ip-address:3009'\n        const ws = new WebSocket(address);\n        const frameImg = document.getElementById('frame');\n\n        ws.onmessage = (event) =\u003e {\n            console.log('Received frame data:', event.data);\n            const blob = new Blob([event.data], { type: 'image/jpeg' });\n            frameImg.src = URL.createObjectURL(blob);\n        };\n    \u003c/script\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n\n\n## 怎么启动\n\n烧录上传至服务器后\n\n- 访问arduino 115200串口输出的wifi地址 加上 :81/stream\n- 然后访问前端界面就好了","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felectroluxcode%2Fesp32cam-node-server","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Felectroluxcode%2Fesp32cam-node-server","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felectroluxcode%2Fesp32cam-node-server/lists"}