用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模块天线,才解决了这个问题。
这个模块整体不错,因为非常便宜,而且简单易用,只不过由于存在信号中断问题,使用距离会受到一定的限制。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 | #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打印、焊接等等。所以,它不仅能让你了解某一个技术领域,更重要的是让你了解不同的技术领域如何交互作用,从而实现一个完整的项目。
该项目设计简单,只要具备电路、焊接、研磨、设计等方面的基本技能,每个人都可以完成。最关键的要素还是时间。
【推荐】100%开源!大型工业跨平台软件C++源码提供,建模,组态!
【推荐】2025 HarmonyOS 鸿蒙创新赛正式启动,百万大奖等你挑战
【推荐】博客园的心动:当一群程序员决定开源共建一个真诚相亲平台
【推荐】开源 Linux 服务器运维管理面板 1Panel V2 版本正式发布
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有调度器的协程不是好协程,零基础深入浅出 C++20 协程
· 别做抢活的导演:代码中的抽象层次原则
· 从 Redis 客户端超时到 .NET 线程池挑战
· C23和C++26的#embed嵌入资源指南
· 「EF Core」框架是如何识别实体类的属性和主键的
· 阿里巴巴为什么禁止超过3张表join?
· 博客园众包线下沙龙第1期:云栖开发者基地,共建技术新天地
· 让 AI 帮我部署网站,太方便了!
· 别做抢活的导演:代码中的抽象层次原则
· .NET周刊【7月第1期 2025-07-06】