前言
我们学校生产实习项目之一——智能环境节点检测,我负责本模块的开发。这是我第一次遇到单片机TTL转RS485模块,其中的难点在
- ESP32上通过ttl转rs485接口实现串口接收modbus格式的数据。
- 编写代码以解析Modbus数据帧,提取风速和风向数据。
- 将解析后的风速和风向数据进行处理和显示,风速单位为m/s,风向为16方位法如N(北风),NNE(东北偏北风),NE(东北风)等。
- 将代码封装成头文件和接口函数,使用者调用函数即可实现功能。
软硬件简要介绍
一、搭建ESP32开发环境
VScode + platfromIO
1.vscode官网选择相应系统版本安装即可,我使用的是压缩包方式,下载后直接解压即可使用,无需安装
https://code.visualstudio.com/#alt-downloads
2.相关插件的安装和配置
打开vs-code后安装所需要的插件:
下面四个直接安装即可,最后一个是vscode界面汉化插件。
并下载PlatformIO插件
4.安装串口监视插件 Serial Monitor:
3.使用PlatformIO进行项目的创建和编译
根据箭头选择Platformio主页打开,右侧四个选项分别为创建新项目,导入arduinoi项目以及打开Platformio项目和打开项目示例。
在此我们选择创建新项目。
之后选项开发板,项目名称即可。开发板选择ESP32 Dev Module选项,项目名称和储存路径不要带中文。
4.外设库的下载
在使用esp32时我们会用到各种外设库,外设库的安装方法如下:
进入plat主页选择libraries进入外设库页面
下载Arduino库
二、硬件介绍
1.ESP32
大家的芯片型号不同,可能引脚分布不同,我们此次通过UART1:GPIO16、GPIO17读取转换后的RS485信号。
2.单片机TTL转RS485模块
- 使用MAX485芯片
- 自动控制流向
- 可连接多个传感器
- 输入3.3V或者5V
接线说明
单片机16号引脚——TXD , 17号引脚——RXD ,5V供电引脚——VCC , GUN——GUN 。
电路连接图
风向风速变送器采用RS485硬件接口,协议层兼容标准的工业Modbus-Rtu协议。被广泛应用于温室、环境保护、气象站、船舶、码头、养殖等环境的风向风速测量。
两个传感器需要外接12V适配器电源,除了图中这样,直接连接一块12V电源(电池)也可。
代码实现
头文件WindDet.h 及关键变量和功能函数设计
#ifndef __WINDDET_H
#define __WINDDET_H
#include <Arduino.h>
// float wspeed;
// String wdirStr;
#define TX1_PIN 16
#define RX1_PIN 17 //接16.17号引脚
// 定义风速和风向的查询命令
const uint8_t windSpeedQuery[] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x02, 0xC4, 0x0B};
const uint8_t windDirectionQuery[] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x02, 0xC4, 0x0B};
String convertWindDirection(int wdir); // 将风向整数值转换为字符串表示的函数
void ReadWindSpeed(HardwareSerial &mySerial1);// 读取风速的函数,通过串行端口与风速传感器通信
void ReadWindDirection(HardwareSerial &mySerial2); // 读取风向的函数,通过串行端口与风向传感器通信
#endif
代码简要设计
-
发送查询命令:函数
ReadWindSpeed
和ReadWindDirection
分别向风速和风向传感器发送预定义的查询命令。 -
等待响应:使用
delay
函数等待传感器有足够的时间响应。 -
读取数据:检查串口是否有数据可读,如果有,则读取数据到缓冲区。
-
数据校验:检查接收到的数据长度和格式是否正确,以确保数据的完整性。
-
提取和处理数据:从响应数据中提取风速和风向的值,并将其转换为实际的物理量。
-
输出结果:通过串口输出风速和风向的值。
#include "WindDet.h"
// 读取风速的函数,通过HardwareSerial对象与风速传感器通信
void ReadWindSpeed(HardwareSerial &mySerial1)
{
// 重置事件队列,以防之前的串口事件干扰当前通信
mySerial1.eventQueueReset();
// 向风速传感器发送查询命令,请求风速数据
mySerial1.write(windSpeedQuery, sizeof(windSpeedQuery));
// 等待一段时间以便传感器有足够的时间响应
delay(1000);
// 检查串口是否有数据可读
if (mySerial1.available())
{
// 定义一个缓冲区,用于存储从串口读取的数据
uint8_t buffer[128];
// 从串口读取数据到缓冲区,并获取读取的字节数
int len = mySerial1.readBytes(buffer, sizeof(buffer));
// 校验数据格式是否正确(数据长度、设备地址、功能码)
if (len > 0 && len == 9 && buffer[0] == 0x01 && buffer[1] == 0x03)
{
// 从响应数据中提取风速值(风速数据格式假设为2个字节的整数)
int fs10 = buffer[3] * 256 + buffer[4];
// 将提取的风速值转换为实际的风速(假设原数据单位为0.1m/s)
float wspeed = fs10 / 10.0;
// 通过串口输出实际的风速值
Serial.print("Wind Speed: ");
Serial.print(wspeed);
Serial.println(" m/s");
}
else
{
// 如果数据格式不正确,输出错误信息
Serial.println("Failed to read wind speed");
}
}
else
{
// 如果没有接收到任何数据,输出无响应信息
Serial.println("No response for wind speed");
}
}
// 读取风向的函数,通过HardwareSerial对象与风向传感器通信
void ReadWindDirection(HardwareSerial &mySerial2)
{
// 向风向传感器发送查询命令,请求风向数据
mySerial2.write(windDirectionQuery, sizeof(windDirectionQuery));
// 等待一段时间以便传感器有足够的时间响应
delay(150);
// 检查串口是否有数据可读
if (mySerial2.available())
{
// 定义一个缓冲区,用于存储从串口读取的数据
uint8_t buffer[128];
// 从串口读取数据到缓冲区,并获取读取的字节数
int len = mySerial2.readBytes(buffer, sizeof(buffer));
// 校验数据格式是否正确(数据长度、设备地址、功能码)
if (len > 0 && len == 9 && buffer[0] == 0x01 && buffer[1] == 0x03)
{
// 从响应数据中提取风向值(风向数据格式假设为2个字节的浮点数)
float windDir = (buffer[3] * 256 + buffer[4]) / 10;
// 提取风向的整数值
int wdir = (buffer[5] * 256 + buffer[6]);
// 输出风向的数值和字符串表示
Serial.printf("Wind direction: %d(%.2f)\n", wdir, windDir);
// 将风向整数值转换为风向字符串
String wdirStr = convertWindDirection(wdir);
Serial.print("Wind Direction: ");
Serial.println(wdirStr);
}
else
{
// 如果数据格式不正确,输出错误信息
Serial.println("Failed to read wind direction");
}
}
else
{
// 如果没有接收到任何数据,输出无响应信息
Serial.println("No response for wind direction");
}
}
// 将风向整数值转换为字符串表示的函数
String convertWindDirection(int wdir)
{
// 使用switch语句,根据风向整数值返回对应的风向字符串
switch (wdir)
{
case 0: return "N"; // 北
case 1: return "NNE"; // 北北东
case 2: return "NE"; // 东北
case 3: return "ENE"; // 东北东
case 4: return "E"; // 东
case 5: return "ESE"; // 东南东
case 6: return "SE"; // 东南
case 7: return "SSE"; // 南南东
case 8: return "S"; // 南
case 9: return "SSW"; // 南南西
case 10: return "SW"; // 西南
case 11: return "WSW"; // 西西南
case 12: return "W"; // 西
case 13: return "WNW"; // 西西北
case 14: return "NW"; // 西北
default: return "NNW"; // 北北西
}
}
最后采集到的风向风速信息,在串口打印输出