【ESP32S3】esp32获取串口数据并通过http上传到前端

通过前面的学习(前面没发过,因为其实就是跑它的demo)了解到串口配置以及开启线程实现功能的工作流程,与此同时还有esp32作为STA节点,将数据通过http发送到服务器。
将这两者联合
其实是可以得到一个:esp32获取串口数据并通过http上传到前端,这样的功能的。假设收到的数据是温湿度数据。
文章食用提醒:
本文用到的ESP框架是ESP-IDF,服务器端处理代码格式是js,数据库采用mongoDB。

http part

#define MAX_HTTP_RECV_BUFFER 512
#define MAX_HTTP_OUTPUT_BUFFER 2048static const char *TAG = "HTTP_CLIENT 0313";
static char response_data[1024]; // 自定义缓存空间储存一次响应数据
static int recived_len = 0;      // 自定义变量储存一次响应中接收到分片数据的累计偏移
// http客户端的事件处理回调函数
static esp_err_t http_client_event_handler(esp_http_client_event_t *evt)
{switch (evt->event_id){case HTTP_EVENT_ON_CONNECTED:ESP_LOGI(TAG, "connected to web-server");recived_len = 0;break;case HTTP_EVENT_ON_DATA:if (evt->user_data){memcpy(evt->user_data + recived_len, evt->data, evt->data_len); // 将分片的每一片数据都复制到user_datarecived_len += evt->data_len;//累计偏移更新}break;case HTTP_EVENT_ON_FINISH:ESP_LOGI(TAG, "finished a request and response!");recived_len = 0;break;case HTTP_EVENT_DISCONNECTED:ESP_LOGI(TAG, "disconnected to web-server");recived_len = 0;break;case HTTP_EVENT_ERROR:ESP_LOGE(TAG, "error");recived_len = 0;break;default:break;}return ESP_OK;
}
char* create_json_from_data(float temperature, float humidity) {// 创建根对象cJSON *root = cJSON_CreateObject();// 向JSON对象中添加键值对cJSON_AddNumberToObject(root, "temperature", temperature);cJSON_AddNumberToObject(root, "humidity", humidity);// 将cJSON对象转换为字符串char *json_data = cJSON_Print(root);// 释放cJSON对象占用的内存cJSON_Delete(root);return json_data;
}

uart part

#define CONFIG_UART_TXD 4
#define CONFIG_UART_RXD 5
#define UART_PIN_RTS (-1)
#define UART_PIN_CTS (-1)
#define CONFIG_UART_PORT_NUM      2
#define CONFIG_UART_BAUD_RATE     115200
#define CONFIG_TASK_STACK_SIZE    3072
#define BUF_SIZE (1024)
#define QUEUE_LENGTH 10
static QueueHandle_t xQueue = NULL;
// 假设收到的数据是温湿度数据
typedef struct data_dht11
{float temperature;float humidity;
}data_t;static void uart_rx_task(void *arg) 
{// 配置串口uart_config_t uart_config = {.baud_rate = CONFIG_UART_BAUD_RATE,.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,};int intr_alloc_flags = 0;#if CONFIG_UART_ISR_IN_IRAMintr_alloc_flags = ESP_INTR_FLAG_IRAM;#endifESP_ERROR_CHECK(uart_driver_install(CONFIG_UART_PORT_NUM, BUF_SIZE * 2, 0, 0, NULL, 0));ESP_ERROR_CHECK(uart_param_config(CONFIG_UART_PORT_NUM, &uart_config));ESP_ERROR_CHECK(uart_set_pin(CONFIG_UART_PORT_NUM, CONFIG_UART_TXD, CONFIG_UART_RXD, UART_PIN_RTS, UART_PIN_CTS));uint8_t *data = (uint8_t *)malloc(BUF_SIZE);// 接收数据,把数据存放在队列里while (1) {int len = uart_read_bytes(CONFIG_UART_PORT_NUM, data, (BUF_SIZE - 1), 20 / portTICK_PERIOD_MS);if(len > 0) {data[len] = '\0';data_t item;sscanf((char*)data, "%f %f", &item.temperature, &item.humidity); // 假设数据格式为"temperature humidity"xQueueSend(xQueue, &item, portMAX_DELAY);}vTaskDelay(pdMS_TO_TICKS(10)); // 让出CPU时间片,分给其他任务}free(data);
}

main part

void app_main(void)
{esp_err_t ret;nvs_flash_init();esp_netif_init();esp_event_loop_create_default();example_connect();xQueue = xQueueCreate(QUEUE_LENGTH, sizeof(data_t));if (xQueue == NULL) {ESP_LOGE(TAG, "Failed to create queue");return;}xTaskCreate(uart_rx_task, "uart_rx_task", 2048, NULL, configMAX_PRIORITIES-1, NULL);// http配置const esp_http_client_config_t cfg = {.url = "http://124.223.186.76:3000",.event_handler = http_client_event_handler,.user_data = response_data,.disable_auto_redirect = true, // 根据需求选择是否禁用自动重定向.transport_type = HTTP_TRANSPORT_OVER_TCP, // 强制使用TCP传输.timeout_ms = 10000, // 设置超时时间为10秒};//使用http服务器配置参数对http客户端初始化esp_http_client_handle_t httpclient = esp_http_client_init(&cfg);// 进入循环接收串口数据并发给服务器上while (true) {data_t item;if(xQueueReceive(xQueue, &item, portMAX_DELAY) == pdPASS){// 调用函数创建JSON格式的数据char *json_data = create_json_from_data(item.temperature, item.humidity);// 设置HTTP请求的各种参数esp_http_client_set_method(httpclient, HTTP_METHOD_POST);esp_http_client_set_url(httpclient, "/add");// 添加或更新"Connection"头为"close"esp_http_client_set_header(httpclient, "Connection", "close");// 设置请求头esp_http_client_set_header(httpclient, "Content-Type", "application/json");// 设置请求体为刚刚创建的JSON数据esp_http_client_set_post_field(httpclient, json_data, strlen(json_data));// 初始化重试计数器int max_retries = 3; // 最大重试次数esp_err_t ret;for(int retry = 0; retry <= max_retries; ++retry) {ret = esp_http_client_perform(httpclient);if(ret == ESP_OK) {// 请求成功,打印响应数据printf("POST:%s\n", response_data);ESP_LOGD(TAG,"HTTP POST Status = %d, content_length = %lld",esp_http_client_get_status_code(httpclient),esp_http_client_get_content_length(httpclient));break; // 成功后退出循环} else {if(retry < max_retries) {// 如果还有剩余重试次数,则等待一段时间后重试ESP_LOGW(TAG, "Attempt %d failed, retrying in 1 second...", retry + 1);vTaskDelay(pdMS_TO_TICKS(1000)); // 等待1秒后再重试} else {// 达到最大重试次数,记录错误信息ESP_LOGE(TAG, "Error occurred during HTTP request after %d retries, failed: %s", max_retries, esp_err_to_name(ret));}}}free(json_data); // 释放动态分配的内存}else {// 处理超时或其他错误情况ESP_LOGW(TAG, "Failed to receive data from queue.");}}esp_http_client_cleanup(httpclient);//清空http客户端描述符vQueueDelete(xQueue); // 删除队列
}

merge code

const express = require('express')
const bodyParser = require('body-parser')
const { MongoClient } = require('mongodb');
const request = require('request')
const fs = require('fs')const app = express()
app.use(bodyParser.json())// MongoDB URI 和客户端初始化
const uri = "mongodb://admin:123456@localhost:27017/myDatabase?authSource=admin";
const client = new MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology: true });let db; // 定义一个变量用于存储数据库引用async function startServer() {try {await client.connect(); // 连接到MongoDBconsole.log("Connected to database");db = client.db('myDatabase'); // 获取数据库引用// 监听端口app.listen(3000, '0.0.0.0', () => {console.log('mwt server running at http://124.223.186.76:3000');});} catch (err) {console.error("Failed to connect to the database:", err);process.exit(1); // 如果无法连接数据库,则退出程序}
}startServer();app.get('/add', (req, res) => {const x = req.query.x;const y = req.query.y;if (!x || !y) {return res.status(400).json({ result: false, message: "Missing parameters" });}res.json({result: true,method: "GET",message: Number(x) + Number(y)});
});// 处理外设数据上传的POST请求
app.post('/add', async (req, res) => {const data = req.body; // 直接获取到JSON对象console.log("Received data:", data); // 新增日志记录if (!data.temperature || !data.humidity) {return res.status(400).json({ result: false, message: "Missing parameters" });}try {if (!db) {return res.status(500).json({ result: false, message: "Database connection not established" });}// 插入文档到集合'readings'const collection = db.collection('readings'); // 使用之前定义的db变量await collection.insertOne(data);console.log(`A document was inserted with the _id: ${data._id}`);// 写到当前路径下的log里fs.appendFile('sensor_data.log', JSON.stringify(data) + '\n', (err) => {if (err) throw err;});res.json({result: true,method: "POST",message: "Data received and saved successfully"});} catch (err) {console.error("Failed to save data:", err.stack);res.status(500).json({ result: false, message: "Failed to save data" });}
});// 获取所有读数并显示在前端
app.get('/readings', async (req, res) => {try {const collection = db.collection('readings');// 查找所有文档const readings = await collection.find({}).toArray();// 返回HTML页面或JSON数据res.send(`<html><head><title>Sensor Readings</title></head><body><h1>Sensor Readings</h1><ul>${readings.map(r => `<li>Temperature: ${r.temperature}, Humidity: ${r.humidity}</li>`).join('')}</ul></body></html>`);} catch (err) {console.error("Failed to fetch data:", err);res.status(500).send("Error fetching data");}
});

接着在Linux下写服务器的处理内容

index.js code

const express = require('express')
const bodyParser = require('body-parser')
const { MongoClient } = require('mongodb');
const request = require('request')
const fs = require('fs')const app = express()
app.use(bodyParser.json())// MongoDB URI 和客户端初始化,这里的账号密码记得填你自己的
const uri = "mongodb://yourusername:yourpasswd@localhost:27017/myDatabase?authSource=admin";
const client = new MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology: true });let db; // 定义一个变量用于存储数据库引用async function startServer() {try {await client.connect(); // 连接到MongoDBconsole.log("Connected to database");db = client.db('myDatabase'); // 获取数据库引用,这里的myDatabase是数据库名// 监听端口app.listen(3000, '0.0.0.0', () => {console.log('mwt server running at http://124.223.186.76:3000');});} catch (err) {console.error("Failed to connect to the database:", err);process.exit(1); // 如果无法连接数据库,则退出程序}
}startServer();// 处理外设数据上传的POST请求
app.post('/add', async (req, res) => {const data = req.body; // 直接获取到JSON对象console.log("Received data:", data); // 新增日志记录if (!data.temperature || !data.humidity) {return res.status(400).json({ result: false, message: "Missing parameters" });}try {if (!db) {return res.status(500).json({ result: false, message: "Database connection not established" });}// 插入文档到集合'readings'const collection = db.collection('readings'); // 使用之前定义的db变量,这里的readings是表名await collection.insertOne(data);console.log(`A document was inserted with the _id: ${data._id}`);// 写到当前路径下的log里fs.appendFile('sensor_data.log', JSON.stringify(data) + '\n', (err) => {if (err) throw err;});res.json({result: true,method: "POST",message: "Data received and saved successfully"});} catch (err) {console.error("Failed to save data:", err.stack);res.status(500).json({ result: false, message: "Failed to save data" });}
});
// 获取所有读数并简单地显示在前端,也可以另创一个html文件或新建一个web工程按实际情况导进去
app.get('/readings', async (req, res) => {try {const collection = db.collection('readings');  //表名是readings// 查找所有文档const readings = await collection.find({}).toArray();// 返回HTML页面或JSON数据res.send(`<html><head><title>Sensor Readings</title></head><body><h1>Sensor Readings</h1><ul>${readings.map(r => `<li>Temperature: ${r.temperature}, Humidity: ${r.humidity}</li>`).join('')}</ul></body></html>`);} catch (err) {console.error("Failed to fetch data:", err);res.status(500).send("Error fetching data");}
});

在服务器部署nodejs,并安装配置mongoDB环境,这样前端就可以从mongoDB拿到数据并显示出来了。
假设已经安装好nodejs,也装好了mongoDB。
新建一个目录,我这里叫my_node_app0314,然后把上面的js处理脚本放进这个文件夹里,这里我把它命名为index.js
1
然后启动应用

node index.js

如果看到active running就说明启动好了

ubuntu@VM-12-13-ubuntu:~$ sudo systemctl status my_node_app0314.service # 看状态
● my_node_app0314.service - My Node.js ApplicationLoaded: loaded (/etc/systemd/system/my_node_app0314.service; enabled; vendor preset: enabled)Active: active (running) since Fri 2025-03-21 10:43:59 CST; 24s agoMain PID: 316472 (node)Tasks: 11 (limit: 8816)Memory: 24.6MCGroup: /system.slice/my_node_app0314.service└─316472 /usr/bin/node /home/ubuntu/my_node_app0314/index.jsMar 21 10:43:59 VM-12-13-ubuntu systemd[1]: Started My Node.js Application.
Mar 21 10:44:00 VM-12-13-ubuntu node[316472]: (node:316472) [MONGODB DRIVER] Warning: useNewUrlParser i>
Mar 21 10:44:00 VM-12-13-ubuntu node[316472]: (Use `node --trace-warnings ...` to show where the warnin>
Mar 21 10:44:00 VM-12-13-ubuntu node[316472]: (node:316472) [MONGODB DRIVER] Warning: useUnifiedTopolog>
Mar 21 10:44:00 VM-12-13-ubuntu node[316472]: Connected to database
Mar 21 10:44:00 VM-12-13-ubuntu node[316472]: mwt server running at http://124.223.186.76:3000
lines 1-15/15 (END)

最后

编译运行esp32s3,并打开浏览器
3
很好的是esp32s3支持很多的i2c扩展,我这里用串口只是为了方便,dht11是单总线的,以上数据来自串口助手。
【全文完】

参考链接

:ESP32+idf开发之WIFI通信入门(5)HTTP通信
跑了参考链接的代码
这个博主的文章代码亲测可用,在这里也很谢谢他。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/74489.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《鸿蒙携手AI:解锁智慧出行底层逻辑》

在科技飞速发展的当下&#xff0c;智慧出行成为人们对未来交通的美好期许&#xff0c;而鸿蒙系统与人工智能的深度融合&#xff0c;正为这一愿景的实现提供强大助力。从技术原理角度深入剖析&#xff0c;鸿蒙系统究竟如何支撑人工智能在智慧出行场景中的应用呢&#xff1f;这背…

MyBatis-Plus缓存机制深度解析与SpringBoot整合实战

一、MyBatis-Plus缓存机制全景解析 MyBatis-Plus在MyBatis原生缓存基础上进行了深度增强,形成了多层次的缓存体系: 1. 缓存层级架构 应用层 ├── MP扩展缓存(多租户/逻辑删除) ├── 二级缓存(Mapper级别,跨Session共享) └── 一级缓存(SqlSession级别,默认开…

Day38 | 1365. 有多少小于当前数字的数字、941. 有效的山脉数组、1207. 独一无二的出现次数、283. 移动零、189. 轮转数组

1365. 有多少小于当前数字的数字 题目链接&#xff1a;1365. 有多少小、于当前数字的数字 - 力扣&#xff08;LeetCode&#xff09; 题目难度&#xff1a;简单 代码&#xff1a; class Solution {public int[] smallerNumbersThanCurrent(int[] nums) {Map<Integer,Inte…

数据人的进阶之路:四年数仓实践与成长思考

前言 在数据仓库开发的过程中&#xff0c;常常会遇到很多值得思考的问题&#xff0c;它们不仅关乎技术的深度&#xff0c;也涉及业务理解、个人的成长&#xff0c;甚至是数据行业未来的价值。回顾过去的经历&#xff0c;有很多问题反复出现&#xff0c;甚至成为绕不开的课题&am…

大文件分片上传及断点续传实现

使用 支持分片上传及断点续传 前端使用 vue 2 后端使用 springboot 源码在私信

图解AUTOSAR_SWS_IOHardwareAbstraction

AUTOSAR IO硬件抽象层详解 基于AUTOSAR标准的IO硬件抽象层设计与实现指南 目录 1. 概述2. 架构设计 2.1 模块架构概览2.2 内部组件结构2.3 与其他模块的交互接口 3. 状态机 3.1 状态定义3.2 状态转换3.3 状态行为 4. ADC信号处理流程 4.1 初始化流程4.2 转换请求和处理4.3 通知…

Python正则表达式(一)

目录 一、正则表达式的基本概念 1、基本概念 2、正则表达式的特殊字符 二、范围符号和量词 1、范围符号 2、匹配汉字 3、量词 三、正则表达式函数 1、使用正则表达式&#xff1a; 2、re.match()函数 3、re.search()函数 4、findall()函数 5、re.finditer()函数 6…

北京交通大学第三届C语言积分赛

作者有言在先&#xff1a; 题解的作用是交流思路&#xff0c;不是抄作业的。可以把重点放在思路分析上而不是代码上&#xff0c;毕竟每个人的代码风格是不一样的&#xff0c;看别人的代码就跟做程序填空题一样。先看明白思路再看代码。 还有就是&#xff0c;deepseek真的很好用…

机器学习之条件概率

1. 引言 概率模型在机器学习中广泛应用于数据分析、模式识别和推理任务。本文将调研几种重要的概率模型,包括EM算法、MCMC、朴素贝叶斯、贝叶斯网络、概率图模型(CRF、HMM)以及最大熵模型,介绍其基本原理、算法流程、应用场景及优势。 2. EM算法(Expectation-Maximizati…

硬件基础--03_电流

电流 十九世纪初:[电流方向]是指正电荷的移动方向。 后来:对于金属导体&#xff0c;正电荷没移动&#xff0c;其实是电子在移动。 为了定义的统一性[电流方向]仍然定义为正电荷的移动方向 所以:[电流方向]与[电子移动方向]是相反的。 概念:电荷的定向移动&#xff0c;形成了电…

multi paxos协议

1. Redo Log 同步的核心目标 ​数据一致性&#xff1a;确保所有副本在事务提交后具有相同的数据视图。​容错性&#xff1a;在主副本故障时&#xff0c;从副本能快速接管并恢复数据。​高吞吐&#xff1a;通过批量同步和并行处理提升效率。 2. Multi Paxos 协议的同步流程 M…

借壹起航东风,中国工厂出海开启新征程

在经济全球化不断深入的当下&#xff0c;中国工厂正以积极的姿态投身海外市场&#xff0c;渴望在全球商业版图中占据一席之地&#xff0c;绽放独特的光彩。然而&#xff0c;出海之路充满了挑战与艰辛&#xff0c;品牌塑造困难重重、询盘量不稳定、营销成本居高不下等问题&#…

【MySQL】监控MySQL

目录 使用状态变量监控MySQL 使用性能模式&#xff08;Performance Schema&#xff09;监控MySQL 1.性能模式 2.性能模式设置表 3.sys模式 使用状态变量监控MySQL 使用 show status 语句评估系统运行状况。 可以添加范围修饰符global或session来显示全局或本地状态信息。…

在linux系统上卸载并重新安装Docker及配置国内镜像源指

前言 Docker 作为容器化技术的核心工具&#xff0c;广泛应用于开发、测试和部署环境。但在某些情况下&#xff08;如版本冲突、配置错误等&#xff09;&#xff0c;可能需要彻底卸载并重新安装 Docker。此外&#xff0c;国内用户直接访问 Docker 官方镜像源可能速度较慢&#…

Mysql内置函数篇

&#x1f3dd;️专栏&#xff1a;Mysql_猫咪-9527的博客-CSDN博客 &#x1f305;主页&#xff1a;猫咪-9527-CSDN博客 “欲穷千里目&#xff0c;更上一层楼。会当凌绝顶&#xff0c;一览众山小。” 目录 7.函数 7.1 日期函数 函数总&#xff1a;​编辑 获得当前日期 获得…

小爱控制OK影视搜索视频

在adb connect ip以后&#xff0c;可以这样打开Ok影视&#xff0c;并且进行控制 pm list packages -3 #只显示第三方 dumpsys package com.fongmi.android.tv |grep Activity #返回 com.fongmi.android.tv/.ui.activity.HomeActivity am start -n com.fongmi.android.tv/.u…

电机倍频曲线的一些奇异特性-原因分析及应用

这里对感应电机倍频曲线的特征进行了说明&#xff0c;然后将其特性用于电机转差率和工况的测量。先给出可以直接利用的结论&#xff1a; 电机的工况和转差率谱线会体现为5x,7x谱线调制在基频附近。两条调制过携带s信息的谱线距离基频谱线的距离。 与真实转速相对同步转速的频差…

双指针技巧在C++中的应用:从基础到进阶

目录 1.简介 2.同向双指针 2.1.数组去重 2.2.最大子数组和 2.3.链表反转 2.4.字符串匹配&#xff08;简单版&#xff09; 3.对向双指针 3.1.两数之和&#xff08;有序数组&#xff09; 3.2.盛最多水的容器 4.快慢指针 4.1.判断链表是否有环 4.2.寻找链表的中间节点…

语言解码双生花:人类经验与AI算法的镜像之旅

大家好&#xff0c;我是吾鳴。 今天吾鳴要给大家分享一份由浙江大学出品的DeepSeek报告&#xff0c;报告从语言的奥秘&#xff0c;人类是如何通过语言来解码世界&#xff0c;AI又是如何理解人类的语言&#xff0c;同时介绍了当下爆火的DeepSeek-V3和DeepSeek-R1两种大模型的进化…

如何避免测试数据准备不充分或不可复用

避免测试数据准备不充分或不可复用的关键方法包括明确数据需求、统一数据管理工具、建立数据复用机制、定期维护更新测试数据以及加强团队沟通与协作。 其中&#xff0c;统一数据管理工具对确保数据质量和复用性尤为重要。例如&#xff0c;许多团队采用专门的测试数据管理工具以…