esp32s3通过串口与JY901S通讯
时间: 2025-08-19 17:09:09 浏览: 7
<think>我们之前讨论了使用I2C接口连接JY901S,现在用户希望了解如何使用串口(UART)连接。根据引用[1],JY901S支持串口和IIC两种接口,串口波特率可调(4800bps~230400bps)。下面将提供使用串口的指南。
### 硬件连接
```
ESP32-S3 JY901S
=======================
3.3V -----> VCC
GND -----> GND
IO18 -----> TXD (传感器) // 注意:JY901S的TXD接ESP32-S3的RXD
IO17 -----> RXD (传感器) // JY901S的RXD接ESP32-S3的TXD
```
> 注意:串口连接需要交叉,即传感器的TXD接ESP32的RXD,传感器的RXD接ESP32的TXD。同时,JY901S的工作电压为3.3V,与ESP32-S3兼容。
### 串口配置
根据JY901S文档,默认串口参数:
- 波特率:9600(可调整,但初始默认9600)
- 数据位:8位
- 停止位:1位
- 无校验位
### PlatformIO配置
在`platformio.ini`中,我们不需要额外库,因为使用Arduino的Serial库:
```ini
[env:esp32-s3-devkitc-1]
platform = espressif32
board = esp32-s3-devkitc-1
framework = arduino
monitor_speed = 115200
```
### 代码实现
JY901S通过串口输出二进制数据包,我们需要按照其协议解析。数据包格式为:
- 包头:0x55
- 功能字(1字节):指示数据类型(如0x53表示欧拉角)
- 数据(8字节):根据功能字解析
- 校验和(1字节):包头到数据所有字节和的低8位
以获取欧拉角(航向角、俯仰角、横滚角)为例,数据包共11字节:
```
0x55, 0x53, [8字节数据], [校验和]
```
其中航向角(Yaw)在数据中的第5、6字节(小端格式)。
创建`main.cpp`:
```cpp
#include <Arduino.h>
// 定义串口引脚
#define RXD_PIN 18
#define TXD_PIN 17
// 数据包结构
#pragma pack(push, 1)
typedef struct {
uint8_t header; // 0x55
uint8_t function; // 功能字
union {
struct {
float roll; // 横滚角 (度)
float pitch; // 俯仰角 (度)
float yaw; // 航向角 (度)
} euler;
uint8_t data[8]; // 原始数据
};
uint8_t checksum; // 校验和
} JY901S_Packet;
#pragma pack(pop)
// 全局变量
HardwareSerial SerialPort(1); // 使用UART1
JY901S_Packet packet;
bool packetReady = false;
uint8_t buffer[sizeof(JY901S_Packet)] = {0};
uint8_t bufferIndex = 0;
// 计算校验和
uint8_t calcChecksum(const uint8_t* data, uint8_t len) {
uint8_t sum = 0;
for (uint8_t i = 0; i < len; i++) {
sum += data[i];
}
return sum;
}
void setup() {
Serial.begin(115200); // 用于调试输出
SerialPort.begin(9600, SERIAL_8N1, RXD_PIN, TXD_PIN); // 连接JY901S
}
void loop() {
// 读取串口数据
while (SerialPort.available()) {
uint8_t c = SerialPort.read();
// 检查包头
if (bufferIndex == 0 && c != 0x55) {
continue; // 不是包头则跳过
}
buffer[bufferIndex++] = c;
// 检查是否接收完一个完整数据包
if (bufferIndex >= sizeof(JY901S_Packet)) {
// 验证校验和
uint8_t sum = calcChecksum(buffer, sizeof(JY901S_Packet)-1);
if (sum == buffer[sizeof(JY901S_Packet)-1]) {
// 复制到结构体
memcpy(&packet, buffer, sizeof(JY901S_Packet));
packetReady = true;
}
bufferIndex = 0; // 重置缓冲区
}
}
// 处理完整数据包
if (packetReady) {
if (packet.header == 0x55) {
switch (packet.function) {
case 0x53: // 欧拉角数据包
Serial.printf("Yaw: %.2f°, Pitch: %.2f°, Roll: %.2f°\n",
packet.euler.yaw, packet.euler.pitch, packet.euler.roll);
break;
// 可以添加其他功能字处理
}
}
packetReady = false;
}
}
```
### 关键点说明
1. **串口初始化**:
- 使用`HardwareSerial`创建UART实例(这里用UART1)
- 引脚定义:`RXD_PIN=18`(接收),`TXD_PIN=17`(发送)
2. **数据包解析**:
- 使用`#pragma pack(1)`确保结构体无内存对齐
- 校验和计算:从包头到数据结束的所有字节和
3. **数据格式**:
- 欧拉角数据包中,航向角(yaw)为浮点数,单位度
- 数据在包内以小端格式存储(JY901S默认)
### 调试技巧
1. **检查串口连接**:
- 若收不到数据,交换TXD/RXD连接线
- 用逻辑分析仪或示波器检查信号
2. **修改波特率**:
```cpp
// 修改波特率(需与传感器设置一致)
SerialPort.begin(115200, SERIAL_8N1, RXD_PIN, TXD_PIN);
```
3. **打印原始数据**:
```cpp
// 在loop()中添加调试输出
Serial.print("Raw: ");
for(int i=0; i<sizeof(buffer); i++){
Serial.printf("%02X ", buffer[i]);
}
Serial.println();
```
### 常见问题解决
- **数据乱码**:检查波特率设置是否匹配(JY901S默认9600,但可能被修改过)
- **校验和错误**:尝试重新上电传感器,或检查电磁干扰
- **数据更新慢**:确保没有其他任务阻塞loop循环
### 扩展功能
通过串口发送命令配置传感器(如修改输出频率):
```cpp
// 设置输出频率为10Hz (0x0A)
void setOutputRate(uint8_t rate) {
uint8_t cmd[] = {0xFF, 0xAA, 0x03, 0x08, rate, 0x00}; // 最后两字节为校验和占位
uint16_t sum = 0;
for (int i=0; i<4; i++) sum += cmd[i+2]; // 从0x03开始计算
cmd[5] = sum & 0xFF; // 低8位
cmd[4] = (sum >> 8) & 0xFF; // 高8位(实际命令中,校验和是16位,低字节在前)
SerialPort.write(cmd, sizeof(cmd));
}
```
> 详细命令格式参考维特智能官方文档[^1]。
§§相关问题§§
1. 如何通过串口命令校准JY901S传感器?
2. ESP32-S3多串口资源如何分配?
3. JY901S的串口数据包格式有哪些类型?
4. 如何提高串口通信的可靠性?
5. 在PlatformIO中如何调试串口通信问题?</think>### ESP32-S3 通过串口连接维特 JY901S 开发指南
#### 一、硬件连接
```
ESP32-S3 JY901S
=======================
3.3V -----> VCC
GND -----> GND
IO18 -----> TXD (传感器) // JY901S的TXD接ESP32的RXD
IO17 -----> RXD (传感器) // JY901S的RXD接ESP32的TXD
```
> **关键说明**:
> 1. 串口连接需**交叉**:传感器的TXD接ESP32的RXD,传感器的RXD接ESP32的TXD
> 2. JY901S工作电压为3.3V,与ESP32-S3完全兼容[^1]
> 3. 默认波特率:**9600bps**(可调范围4800-230400bps)
#### 二、串口协议解析
JY901S通过串口输出二进制数据包,格式如下:
```
0x55 [功能字] [8字节数据] [校验和]
```
航向角数据包示例:
```
55 53 [Roll_L Roll_H Pitch_L Pitch_H Yaw_L Yaw_H TL TH] [CS]
```
- **功能字**:0x53 表示欧拉角数据包
- **航向数据**:第5-6字节(Yaw_L + Yaw_H)
- **校验和**:包头到数据所有字节和的低8位
#### 三、PlatformIO 配置
`platformio.ini` 配置:
```ini
[env:esp32-s3-devkitc-1]
platform = espressif32
board = esp32-s3-devkitc-1
framework = arduino
monitor_speed = 115200
```
#### 四、核心代码实现
```cpp
#include <HardwareSerial.h>
// 使用UART1 (RX=17, TX=18)
HardwareSerial JY901Serial(1);
// 数据包结构体
#pragma pack(push, 1)
typedef struct {
uint8_t header; // 0x55
uint8_t cmd; // 功能字
float roll; // 横滚角
float pitch; // 俯仰角
float yaw; // 航向角
uint8_t checksum; // 校验和
} EulerPacket;
#pragma pack(pop)
void setup() {
Serial.begin(115200); // 调试输出
JY901Serial.begin(9600, SERIAL_8N1, 17, 18); // RX=17, TX=18
}
void loop() {
if (JY901Serial.available()) {
static uint8_t buffer[sizeof(EulerPacket)];
static uint8_t index = 0;
buffer[index] = JY901Serial.read();
// 检查包头
if (index == 0 && buffer[0] != 0x55) return;
if (++index >= sizeof(EulerPacket)) {
index = 0;
EulerPacket* packet = (EulerPacket*)buffer;
// 校验数据
uint8_t sum = 0;
for (int i = 0; i < sizeof(EulerPacket) - 1; i++) {
sum += buffer[i];
}
if (sum == packet->checksum && packet->cmd == 0x53) {
Serial.printf("航向角: %.2f°\n", packet->yaw);
}
}
}
}
```
#### 五、关键处理逻辑
1. **数据包同步**:
```cpp
// 检测包头0x55
if (index == 0 && buffer[0] != 0x55) return;
```
2. **校验和验证**:
```cpp
uint8_t sum = 0;
for (int i = 0; i < sizeof(EulerPacket) - 1; i++) {
sum += buffer[i];
}
if (sum != packet->checksum) return; // 丢弃无效数据
```
3. **航向角转换**:
```cpp
// 原始数据为小端浮点数格式
float heading = packet->yaw; // 直接使用结构体中的浮点数
```
#### 六、调试技巧
1. **原始数据打印**(调试时启用):
```cpp
Serial.print("Raw: ");
for (int i = 0; i < sizeof(EulerPacket); i++) {
Serial.printf("%02X ", buffer[i]);
}
Serial.println();
```
2. **波特率扫描**(当不确定波特率时):
```cpp
void detectBaudRate() {
const long rates[] = {4800, 9600, 19200, 38400, 57600, 115200, 230400};
for (auto rate : rates) {
JY901Serial.begin(rate, SERIAL_8N1, 17, 18);
delay(100);
if (JY901Serial.available()) {
Serial.printf("Detected baud: %ld\n", rate);
return;
}
}
Serial.println("No valid baud rate found!");
}
```
3. **数据包过滤**(只处理有效数据):
```cpp
if (packet->cmd == 0x53) { // 0x53=欧拉角数据包
// 处理航向数据
}
```
#### 七、常见问题解决
1. **无数据接收**:
- 检查TX/RX是否交叉连接
- 尝试不同波特率(使用`detectBaudRate()`函数)
- 确保传感器已供电(LED指示灯应亮起)
2. **数据乱码**:
- 添加延迟确保传感器初始化完成
```cpp
void setup() {
delay(2000); // 等待传感器启动
// 其他初始化...
}
```
- 使用磁环减少电磁干扰
3. **数据跳变**:
- 软件滤波处理
```cpp
#define FILTER_SAMPLES 5
float headingBuffer[FILTER_SAMPLES];
uint8_t bufferIndex = 0;
float filteredHeading(float newValue) {
headingBuffer[bufferIndex++ % FILTER_SAMPLES] = newValue;
float sum = 0;
for (int i = 0; i < FILTER_SAMPLES; i++) sum += headingBuffer[i];
return sum / FILTER_SAMPLES;
}
```
#### 八、高级配置
通过串口发送配置命令(示例设置输出频率为10Hz):
```cpp
void setOutputRate(uint8_t rate) {
uint8_t cmd[] = {0xFF, 0xAA, 0x03, 0x08, rate, 0x00};
// 计算校验和(16位和)
uint16_t sum = 0;
for (int i = 2; i < 5; i++) sum += cmd[i];
cmd[5] = sum >> 8; // 校验和高字节
cmd[6] = sum & 0xFF; // 校验和低字节
JY901Serial.write(cmd, sizeof(cmd));
delay(50);
}
// setup中调用: setOutputRate(0x0A); // 10Hz
```
> 完整命令集请参考[维特智能官方文档](https://wit-motion.yuque.com/wumwnr/ltst03/hg1qgu)[^1]
阅读全文
相关推荐




















