用arduino制作具有无线数据传输功能的气象站
本项目是用arduino开源硬件,来快速制作具有无限数据传输功能的气象站,我之前做过一个带数据记录功能的气象站项目,这次算是升级和改进的版本。
第1步:构想
首先,需要增加从气象站到室内接收器的无线数据传输功能,去掉了SD卡模块,换成Arduino Uno接口扩展板。
这样做的主要原因是为了节省空间,接口扩展版完全兼容Arduino Uno,因此无需使用导线进行连接。气象站支架也进行了重新设计。之前的支架太低,而且不稳,所以我又做了一个新的支架(更高而且更稳)。对于直接安装到气象站支架上的外壳而言,我还增加了一个新的托架。此外,还增加了用于供电的太阳能板。
第2步:原材料
硬件清单:
- Weather station kit(气象站套件,附有风速表/风标/雨斗)
- Arduino Uno开发板
- DFRduino Nano 3.0(兼容Arduino Nano)
- 用于Arduino的RF 433 MHz模块(接收器和发射器)
- 双面洞洞板 58mmx78mm
- SD卡
- 太阳能电源管理模块(5V 1A)
- 半柔性单晶硅太阳能板 (5V 1A) x2
- 接口扩展板 (支持IIC,SPI,TLC,SD卡)
- 一些尼龙扎带
- 安装工具
- IIC/TWI LCD2004液晶模块(Arduino兼容)
- 面包板
- 锂离子电池(我用的是Sanyo 3.7V 2250mAh电池)
- 防水塑料接线盒
- 一些导线
在制作气象站支架时需要:
- 大约3.4米长的钢管(或者钢板)。
- 钢丝(大约4米)
- 钢丝夹(8x)
- 不锈钢螺丝扣(2x)
- fi10钢棒(大约50厘米)
- 钢吊环螺母(4x)
您还将需要以下工具:
- 烙铁
- 螺丝刀
- 钳子
- 电钻
- 焊机
- 角磨机
- 钢刷
第3步:小结
如前文所述,本篇教程是对上篇气象站教程的升级。
如果您想了解如何组装气象站套件,请看组装视频:
第4步:气象站安装方案
还有一个问题,那就是如何安装能够承受室外条件的气象站支架。
关于气象站支架的类型和设计,我做了一些研究。最后我决定使用3米长的钢管来制作支架。通常建议将风速计安装到最高点(大约10米(33英尺)),但是由于我使用的是一体化气象站套件,我选择了套件建议的高度 - 大约3米(10英尺)。
我考虑的主要问题是,这个支架必须模块化且易于拆卸,这样便于转移到其他位置。
组装:
1、先从fi18 3.4m(11.15ft)长钢管开始。在钢管上涂一层酸性除锈剂,对钢管进行除锈处理。
2、2到3小时后,除锈完成,接着把钢管焊接起来。先把吊环螺母焊到钢管两端,然后把钢管放到距地面2米的位置。当然还可以放到更高的位置,但是不能更低,否则靠上的部分就会变得不稳。
3、然后,需要在每一侧制作一个“锚”。为此我使用了两个fi12 50cm(1.64ft)钢棒。在每个钢棒的顶端焊上一个吊环螺母和一个小钢板,这样就可以把它踩到或用锤子砸到地里面。
如图所示:
4、然后,使用钢丝把“锚”上的吊环连到支架两端。先拿来两根1.7 m(5.57ft)长的钢丝,一端用钢丝夹直接固定到吊环螺母上,另一端固定到不锈钢螺丝扣上。不锈钢螺丝扣用于紧固钢丝。
5、然后,使用一个3D打印托架将塑料接线盒安装到支架上。更多详情参见第5步。
6、最后,对每一个钢制零件都涂上两层底漆。在此基础上,您可以涂上任何喜欢的颜色。
第5步:3D打印零件
为使安装支架易于拆卸,需要制作一些3D打印零件。每一个零件都是我亲自设计并使用PLA塑料打印出来的。
塑料接线盒托架
在上一篇教程中,我用钢板制作了托架,但是不是特别实用。所以我决定使用3D打印零件再做一个。一共有五个3D打印零件,损坏的零件可以快速更换。
有了这个托架,塑料接线盒就能直接安装到钢管上。安装高度也可以灵活调节。
温湿度传感器外壳
我需要为温湿度传感器设计一个外壳。在参考网上资料之后,我确定了这个外壳的最终形状。我设计了带托架的史蒂文森百叶箱,这样所有部件都可以安装到钢管上。
它一共包括10个零件。主体底座由两部分组成,顶部是一个“盖子”,这样就可以实现密封,不会进水。每一个零件都是使用PLA耗材打印而成。
第6步:室内数据接收器
本项目的主要升级就是增加了无线数据传输功能。所以还需要增加一个室内数据接收器。
为此,我使用了适合Arduino的430 MHz接收器,然后使用17厘米(6.7英寸)天线对其进行了升级。接着,需要测试一下该模块的通信距离。第一项测试在室内进行,以确定墙壁对信号范围的影响,以及会不会造成信号中断。第二项测试是在室外进行。结果表明,该模块的通信距离在10米(33英尺)以上,远远超出室内接收器的要求。
接收器所需零件:
- Arduino Nano
- Arduino 430 MHz接收器模块
- RTC模块
- LCD显示器
- 一些接头
如图所示,这个接收器可以显示室外温度和湿度、日期和时间。
第7步:测试
在将各零部件组装起来之前,必须进行一些测试。
首先要测试Arduino的发送和接收模块。先得找到合适的代码,然后进行修改以使其符合项目需求。我从最简单的例子开始,从发射器向接收器发送一个字,测试成功之后再发送更多的数据。
然后需要对这两个模块的范围进行测试。先把天线去掉,测试发现通信距离非常短,大约4米(13英尺)。然后把天线加上进行测试。通过相关研究和分析,我认为天线长度最好是17厘米(6.7英寸)。之后分别在室内和室外进行了测试,以确定环境对信号的影响。
最后,将发射器置于室外,接收器置于室内,再进行测试,以确定能否实现良好的室内接收效果。最初有一些信号中断的问题,因为接收到的数据和发射的数据不一致。后来换上从ebay购买的433 Mhz模块天线,才解决了这个问题。
这个模块整体不错,因为非常便宜,而且简单易用,只不过由于存在信号中断问题,使用距离会受到一定的限制。
代码:
#include <SD.h> //SD #include <SPI.h> //SD File myFile; //SD int pinCS = 10; //////////// //LCD #include <Wire.h> #include <LiquidCrystal_I2C.h> #define BACKLIGHT_PIN 3 LiquidCrystal_I2C lcd(0x20, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); /////////// int sensorPin = A0; //battery voltage pin int sensorValue = 0; /////////////////// int sensorPin_solar = A1; //solar panel voltage pin int sensorValue_solar = 0; //////////////// char databuffer[35]; double temp; void getBuffer() //Get weather status data { int index; for (index = 0;index < 35;index ++) { if(Serial.available()) { databuffer[index] = Serial.read(); if (databuffer[0] != 'c') { index = -1; } } else { index --; } } } int transCharToInt(char *_buffer,int _start,int _stop) //char to int) { int _index; int result = 0; int num = _stop - _start + 1; int _temp[num]; for (_index = _start;_index <= _stop;_index ++) { _temp[_index - _start] = _buffer[_index] - '0'; result = 10*result + _temp[_index - _start]; } return result; } int WindDirection() //Wind Direction { return transCharToInt(databuffer,1,3); } float WindSpeedAverage() //air Speed (1 minute) { temp = 0.44704 * transCharToInt(databuffer,5,7); return temp; } float WindSpeedMax() //Max air speed (5 minutes) { temp = 0.44704 * transCharToInt(databuffer,9,11); return temp; } float Temperature() //Temperature ("C") { temp = (transCharToInt(databuffer,13,15) - 32.00) * 5.00 / 9.00; return temp; } float RainfallOneHour() //Rainfall (1 hour) { temp = transCharToInt(databuffer,17,19) * 25.40 * 0.01; return temp; } float RainfallOneDay() //Rainfall (24 hours) { temp = transCharToInt(databuffer,21,23) * 25.40 * 0.01; return temp; } int Humidity() //Humidity { return transCharToInt(databuffer,25,26); } float BarPressure() //Barometric Pressure { temp = transCharToInt(databuffer,28,32); return temp / 10.00; } void setup() { lcd.begin (20,4); lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE); lcd.setBacklight(HIGH); lcd.home (); ////////// Serial.begin(9600); //////// pinMode(pinCS, OUTPUT); // SD Card Initialization if (SD.begin()) { Serial.println("SD card is ready to use."); } else { Serial.println("SD card initialization failed"); return; } ////////// } void loop() { ////////////////// sensorValue = analogRead(sensorPin); //Monitoring battery voltage float voltage = sensorValue*(5.0/1023.0); lcd.setCursor(0,3); //0,3 lcd.print("Voltage bat: "); lcd.print(voltage); lcd.print(" V"); ///////////////// sensorValue_solar = analogRead(sensorPin_solar); float voltage_solar = 2*sensorValue_solar*(5.0/1023.0)-0.07; Serial.println(voltage_solar); // lcd.setCursor(0,2); //This is example how to set your LCD commands // lcd.print("Voltage sol: "); // lcd.print(voltage_solar); // lcd.print(" v"); ///////////////////// getBuffer(); //Begin! /////// if(WindDirection()==0){ Serial.print("Wind Direction: "); Serial.print("SW"); Serial.println(" "); } if(WindDirection()==45){ Serial.print("Wind Direction: "); Serial.print(" W"); Serial.println(" "); } if(WindDirection()==90){ Serial.print("Wind Direction: "); Serial.print("NW"); Serial.println(" "); } if(WindDirection()==135){ Serial.print("Wind Direction: "); Serial.print(" N"); Serial.println(" "); } if(WindDirection()==180){ Serial.print("Wind Direction: "); Serial.print("NE"); Serial.println(" "); } if(WindDirection()==225){ Serial.print("Wind Direction: "); Serial.print(" E"); Serial.println(" "); } if(WindDirection()==270){ Serial.print("Wind Direction: "); Serial.print("SE"); Serial.println(" "); } if(WindDirection()==315){ Serial.print("Wind Direction: "); Serial.print(" S"); Serial.println(" "); } // Serial.print("Wind Direction: "); //Serial.print(WindDirection()); // Serial.println(" "); Serial.print("Average Wind Speed (One Minute): "); Serial.print(WindSpeedAverage()); Serial.println("m/s "); Serial.print("Max Wind Speed (Five Minutes): "); Serial.print(WindSpeedMax()); Serial.println("m/s"); // lcd.setCursor(0,0); // lcd.print("Max Speed"); // lcd.print(" "); //lcd.print(WindSpeedMax()); // lcd.print(" "); // lcd.print("m/s"); Serial.print("Rain Fall (One Hour): "); Serial.print(RainfallOneHour()); Serial.println("mm "); Serial.print("Rain Fall (24 Hour): "); Serial.print(RainfallOneDay()); Serial.println("mm"); Serial.print("Temperature: "); Serial.print(Temperature()); Serial.println("C "); // lcd.setCursor(0,2); // lcd.print("Temperature: "); // lcd.print(Temperature()); // lcd.print("C "); Serial.print("Humidity: "); Serial.print(Humidity()); Serial.println("% "); Serial.print("Barometric Pressure: "); Serial.print(BarPressure()); Serial.println("hPa"); Serial.println(""); Serial.println(""); //// myFile = SD.open("test.txt", FILE_WRITE); if (myFile) { if(WindDirection()==0){ myFile.print("Wind Direction: "); myFile.print("SW"); myFile.println(" "); } if(WindDirection()==45){ myFile.print("Wind Direction: "); myFile.print(" W"); myFile.println(" "); } if(WindDirection()==90){ myFile.print("Wind Direction: "); myFile.print("NW"); myFile.println(" "); } if(WindDirection()==135){ myFile.print("Wind Direction: "); myFile.print(" N"); myFile.println(" "); } if(WindDirection()==180){ myFile.print("Wind Direction: "); myFile.print("NE"); myFile.println(" "); } if(WindDirection()==225){ myFile.print("Wind Direction: "); myFile.print(" E"); myFile.println(" "); } if(WindDirection()==270){ myFile.print("Wind Direction: "); myFile.print("SE"); myFile.println(" "); } if(WindDirection()==315){ myFile.print("Wind Direction: "); myFile.print(" S"); myFile.println(" "); } // myFile.print("Wind Direction: "); // myFile.print(WindDirection()); // myFile.println(" "); myFile.print("Average Wind Speed (One Minute): "); myFile.print(WindSpeedAverage()); myFile.println("m/s "); myFile.print("Max Wind Speed (Five Minutes): "); myFile.print(WindSpeedMax()); myFile.println("m/s"); myFile.print("Rain Fall (One Hour): "); myFile.print(RainfallOneHour()); myFile.println("mm "); myFile.print("Rain Fall (24 Hour): "); myFile.print(RainfallOneDay()); myFile.println("mm"); myFile.print("Temperature: "); myFile.print(Temperature()); myFile.println("C "); myFile.print("Humidity: "); myFile.print(Humidity()); myFile.println("% "); myFile.print("Barometric Pressure: "); myFile.print(BarPressure()); myFile.println("hPa"); myFile.println(""); myFile.println(""); myFile.print("Voltage bat: "); myFile.print(voltage); myFile.println(" V"); myFile.print("Voltage sol: "); myFile.print(voltage_solar); myFile.println(" V"); myFile.close(); // close the file } // if the file didn't open, print an error: else { Serial.println("error opening test.txt"); } delay(100); }
总结
这个项目从最初的想法变成最终的产品,整个过程非常有趣,也很有挑战性。你需要花时间思考不同的选项。所以,整个项目要顺利完成,就需要投入大量时间和精力,才能让它变成你真正想要的样子。
但是类似的项目也提供了很好的机会,让你能够不断扩充升级在设计和电路方面的知识。此外,项目还包含了许多其他技术领域,比如3D建模、3D打印、焊接等等。所以,它不仅能让你了解某一个技术领域,更重要的是让你了解不同的技术领域如何交互作用,从而实现一个完整的项目。
该项目设计简单,只要具备电路、焊接、研磨、设计等方面的基本技能,每个人都可以完成。最关键的要素还是时间。