DIY一个风速测试仪

        这是一个极其简单的东西,仅利用一个普通小电机作为”传感器“。我们知道当转动电机时,将产生一定的电压,往往转动的越快电压越高(并非完全的线性关系)。利用这一特性,使用Arduino的模拟引脚读取电压值就可以换算得到“风速”。

        当然,这只是一个“玩具”,如果你手头有一些二极管、电阻之类的还可以做得更好一些。风速测试的方法有很多是基于小风车转速的,如果你手头有一些红外计数器,可以在风车适当位置增加挡片来获得较为准确的转速,也可以用成品的多线的圆片(多用于电机测速);除此之外,还有很多方法,例如用一些压力传感器测试后换算、用一些高灵敏度的超声波类传感器等等,看手头有什么东西就用什么吧^ ^

        以使用一个小电机为例,结合一些显示屏来进行显示(手头有几块闲置的触摸屏,所以还是用触摸屏)或者直接输出到串口进行观察都可以。

一、硬件

1、Arduino随便一款

2、一个废弃的小电机——轴越灵活越好

3、一个小风车(有很多3D打印资源,找一个打出来,如果没有搓一个纸壳的都行)

4、电阻、二极管、电容、电感(没有问题也不大,最好是有,万一小电机加速度太高害烧了Arduino就赔上了)。我做的这个小玩具确实没有用也还挺抗造的,如果你想用可以搜索一下相关知识,然后选择合适的型号制作一个简单的电路来过滤尖峰、平稳电压、限制输入电压大小等。

5、显示屏(有就用,没有就算了)

二、编码

1、首先,定义一个引脚来读电压,并把它们保存到一个数组里:

void setup() {
  Serial.begin(115200);
  pinMode(A0, INPUT);
  mySerial.begin(9600);
  
  // 初始化数组为0
  for (int i = 0; i < numReadings; i++) {
    readings[i] = 0;
  }
}

        代码中使用了A0引脚,D开头那些不可以,这个工程非常简单没有引脚冲突需要解决,所以查看引脚图随意选一个能读模拟电压的即可。

2、在循环中进行读取

void loop() {
  // 1. 读取当前电压值
  int sensorValue = analogRead(voltagePin);
  float voltage = sensorValue * (5.0 / 1023.0);  // 转换为电压 (0-5V)
  
  // 2. 更新均值计算
  voltageSum -= readings[readIndex];       // 减去旧值
  readings[readIndex] = voltage;          // 存储新值
  voltageSum += voltage;                  // 加上新值
  readIndex = (readIndex + 1) % numReadings; // 循环索引
  
  // 3. 计算5秒均值
  voltageAvg = voltageSum / numReadings;

  float windSpeed = voltageAvg * 86;      // 使用均值计算风速

3、风力等级的换算

        一般,我们直接使用下表就可以了:

int getBeaufortScale(float windSpeed_mps) {
  if (windSpeed_mps < 0.3) return 0;
  else if (windSpeed_mps <= 1.5) return 1;
  else if (windSpeed_mps <= 3.3) return 2;
  else if (windSpeed_mps <= 5.4) return 3;
  else if (windSpeed_mps <= 7.9) return 4;
  else if (windSpeed_mps <= 10.7) return 5;
  else if (windSpeed_mps <= 13.8) return 6;
  else if (windSpeed_mps <= 17.1) return 7;
  else if (windSpeed_mps <= 20.7) return 8;
  else if (windSpeed_mps <= 24.4) return 9;
  else if (windSpeed_mps <= 28.4) return 10;
  else if (windSpeed_mps <= 32.6) return 11;
  else return 12;
}

        至于液晶屏的使用,前面有多篇介绍中有,用几次就熟悉了。一般我们封装几个函数来完成指令发送,而接收部分通常使用一个缓冲区来处理。以下是几个封装范例:


/**
   @brief 发送浮点数指令(格式:page0.t4.txt="23.5" + FF FF FF)
   @param value 浮点数值(如23.5)
   @param objName 目标对象名(如"page0.t4")
*/
void sendFloatAsHex(float value, const char* objName) {
  // 1. 将浮点数转换为保留1位小数的字符串
  char floatStr[8];
  dtostrf(value, 0, 1, floatStr); // 格式: "23.5"

  // 2. 拼接完整指令字符串
  String command = String(objName) + "=\"" + floatStr + "\"";

  // 3. 转换为16进制字节流并发送
  for (size_t i = 0; i < command.length(); i++) {
    mySerial.write(command.charAt(i)); // 逐个字符发送(ASCII即16进制字节)
  }

  // 4. 发送结束符 FF FF FF
  mySerial.write(0xFF);
  mySerial.write(0xFF);
  mySerial.write(0xFF);
}

/**
   @brief 发送整数指令(格式:dim=50 + FF FF FF)
   @param value 整数值(如50)
   @param objName 目标对象名(如"dim")
*/
void sendIntAsHex(int value, const char* objName) {
  // 1. 将整数转换为字符串
  char intStr[8];
  itoa(value, intStr, 10); // 转换为十进制字符串

  // 2. 拼接完整指令字符串
  String command = String(objName) + "=\"" + intStr + "\"";

  // 3. 转换为16进制字节流并发送
  for (size_t i = 0; i < command.length(); i++) {
    mySerial.write(command.charAt(i)); // 逐个字符发送(ASCII即16进制字节)
  }

  // 4. 发送结束符 FF FF FF
  mySerial.write(0xFF);
  mySerial.write(0xFF);
  mySerial.write(0xFF);
}


/**
   @brief 发送字符串指令(格式:Setting.twifiname.txt="winfiname" + FF FF FF)
   @param text 要发送的字符串内容(如"winfiname")
   @param objName 目标对象名(如"Setting.twifiname")
*/
void sendTextCommand(String text, const char* objName) {
  String command = String(objName) + "=\"" + text + "\"";

  for (size_t i = 0; i < command.length(); i++) {
    mySerial.write(command.charAt(i));
  }

  mySerial.write(0xFF);
  mySerial.write(0xFF);
  mySerial.write(0xFF);
}

4、完整代码:

#include <SoftwareSerial.h>
const int voltagePin = A0;

SoftwareSerial mySerial(A2, A3); // RX, TX

// 新增变量用于存储历史数据和均值计算
const int numReadings = 10;      // 5秒内10次记录(每次间隔500ms)
float readings[numReadings];     // 存储历史电压值
int readIndex = 0;               // 当前写入的数组索引
float voltageSum = 0;            // 累加值
float voltageAvg = 0;            // 平均值

void setup() {
  Serial.begin(115200);
  pinMode(A0, INPUT);
  mySerial.begin(9600);
  
  // 初始化数组为0
  for (int i = 0; i < numReadings; i++) {
    readings[i] = 0;
  }
}

void loop() {
  // 1. 读取当前电压值
  int sensorValue = analogRead(voltagePin);
  float voltage = sensorValue * (5.0 / 1023.0);  // 转换为电压 (0-5V)
  
  // 2. 更新均值计算
  voltageSum -= readings[readIndex];       // 减去旧值
  readings[readIndex] = voltage;          // 存储新值
  voltageSum += voltage;                  // 加上新值
  readIndex = (readIndex + 1) % numReadings; // 循环索引
  
  // 3. 计算5秒均值
  voltageAvg = voltageSum / numReadings;
  
  // 4. 输出结果(与原代码一致)
  Serial.print("电压(均值): ");
  Serial.print(voltageAvg);
  Serial.print(" V");

  float windSpeed = voltageAvg * 86;      // 使用均值计算风速
  Serial.print("\t风速: ");
  Serial.print(windSpeed);
  Serial.print(" m/s");

  int windLevel = getBeaufortScale(windSpeed);
  Serial.print("\t等级: ");
  Serial.print(windLevel);
  Serial.println("");

  // 5. 发送数据到串口(使用均值)
  sendFloatAsHex(voltageAvg * 10, "page0.t1.txt");
  sendFloatAsHex(windSpeed, "page0.t4.txt");
  sendIntAsHex(windLevel, "page0.t7.txt");
  
  delay(500);  // 每 0.5 秒读取一次
}

int getBeaufortScale(float windSpeed_mps) {
  if (windSpeed_mps < 0.3) return 0;
  else if (windSpeed_mps <= 1.5) return 1;
  else if (windSpeed_mps <= 3.3) return 2;
  else if (windSpeed_mps <= 5.4) return 3;
  else if (windSpeed_mps <= 7.9) return 4;
  else if (windSpeed_mps <= 10.7) return 5;
  else if (windSpeed_mps <= 13.8) return 6;
  else if (windSpeed_mps <= 17.1) return 7;
  else if (windSpeed_mps <= 20.7) return 8;
  else if (windSpeed_mps <= 24.4) return 9;
  else if (windSpeed_mps <= 28.4) return 10;
  else if (windSpeed_mps <= 32.6) return 11;
  else return 12;
}

/**
   @brief 发送浮点数指令(格式:page0.t4.txt="23.5" + FF FF FF)
   @param value 浮点数值(如23.5)
   @param objName 目标对象名(如"page0.t4")
*/
void sendFloatAsHex(float value, const char* objName) {
  // 1. 将浮点数转换为保留1位小数的字符串
  char floatStr[8];
  dtostrf(value, 0, 1, floatStr); // 格式: "23.5"

  // 2. 拼接完整指令字符串
  String command = String(objName) + "=\"" + floatStr + "\"";

  // 3. 转换为16进制字节流并发送
  for (size_t i = 0; i < command.length(); i++) {
    mySerial.write(command.charAt(i)); // 逐个字符发送(ASCII即16进制字节)
  }

  // 4. 发送结束符 FF FF FF
  mySerial.write(0xFF);
  mySerial.write(0xFF);
  mySerial.write(0xFF);
}

/**
   @brief 发送整数指令(格式:dim=50 + FF FF FF)
   @param value 整数值(如50)
   @param objName 目标对象名(如"dim")
*/
void sendIntAsHex(int value, const char* objName) {
  // 1. 将整数转换为字符串
  char intStr[8];
  itoa(value, intStr, 10); // 转换为十进制字符串

  // 2. 拼接完整指令字符串
  String command = String(objName) + "=\"" + intStr + "\"";

  // 3. 转换为16进制字节流并发送
  for (size_t i = 0; i < command.length(); i++) {
    mySerial.write(command.charAt(i)); // 逐个字符发送(ASCII即16进制字节)
  }

  // 4. 发送结束符 FF FF FF
  mySerial.write(0xFF);
  mySerial.write(0xFF);
  mySerial.write(0xFF);
}


/**
   @brief 发送字符串指令(格式:Setting.twifiname.txt="winfiname" + FF FF FF)
   @param text 要发送的字符串内容(如"winfiname")
   @param objName 目标对象名(如"Setting.twifiname")
*/
void sendTextCommand(String text, const char* objName) {
  String command = String(objName) + "=\"" + text + "\"";

  for (size_t i = 0; i < command.length(); i++) {
    mySerial.write(command.charAt(i));
  }

  mySerial.write(0xFF);
  mySerial.write(0xFF);
  mySerial.write(0xFF);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

清晨曦月

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值