Arduino无线气象站项目

在本教程中,我们将学习如何制作一个基于Arduino的无线气象站。你可以观看下面的视频或阅读下面的书面教程。

概述

使用DHT22传感器测量室外温度和湿度,并且使用NRF24L01收发器模块无线地发送到室内单元的数据。在室内单元,还有另一个DHT22传感器,用于测量室内温度和湿度,以及DS3231实时时钟模块,即使Arduino失去电源,也可以跟踪时间。所有这些数据都印在0.96“OLED显示屏上。

Arduino无线气象站电路图

让我们看一下电路图和这个项目是如何工作的。注意,我已经有详细的教程,如何这些模块的工作,所以更多的细节,你可以检查他们:nrf24l01教程DHT22教程DS3231教程

Arduino无线气象站电路图

您可以从下面的链接获取此项目所需的组件:

必威外围提钱披露:这些是附属链接。作为一名亚马逊助理,我的收入来自符合条件的购买。

实时时钟模块和OLED显示器都使用I2C协议与Arduino通信,因此它们连接到Arduino Nano板上的I2C引脚或模拟引脚4和5。紧挨着NRF24L01收发模块有一个电容,以保持供电更稳定。还有一个上拉电阻连接到DHT22数据引脚,以便传感器正常工作。

至于电源我使用12V直流电源适配器,用于室内机,另一侧,用于为室外单元供电,我使用两个LI-ON电池,产生约7.5V。通过这种配置,室外单元可以在电池放电前10天左右运行,因为我们周期性地传输数据,同时我们将Arduino放到睡眠模式,其中功耗仅为7mA。

定制设计PCB.

为了保持电子元件的组织,根据电路图我设计了必威lol一个定制的PCB使用Easyeda.免费在线电路设计软件。我们可以注意到室内机和室外机都可以使用同一个PCB,只是Arduino板的编程不同。

Arduino气象站定制PCB设计

完成此处的设计后,我们只需导出用于制造PCB的格柏文件。您可以查看Arduino无线气象站的Easyeda项目文件这里

然后我们可以从JLCPCB订购我们的PCB它实际上是这个视频的赞助商。

在这里,我们可以简单地拖放Gerber文件,上传之后,我们可以在Gerber查看器中查看我们的PCB。如果一切正常,我们就可以继续,为我们的PCB选择我们想要的属性,然后我们就可以以合理的价格订购我们的PCB。请注意,如果这是你从JLCPCB的第一个订单,你可以得到最多10个pcb只花2美元。

然而,几天后,多氯联苯就来了。pcb的质量是伟大的,一切都是完全相同的设计。

我开始组装这个项目的电子元件,通过焊接pin头到P必威lolCB上。通过这种方式,我们可以在需要时轻松地连接和断开组件。

然后我也插入并焊接电容器和上拉电阻。使用此步骤完成,我们现在可以简单地将组件附加到PCB的引脚标题上。

接下来,我继续为这个项目制作案例。为此,我使用8mm tick MDF板和使用圆锯,我切所有的碎片大小。

为了精确测量温度和湿度,箱子的侧面必须允许空气进入箱子。所以,使用一个钻和锉刀,我做了几个插槽的侧板上的室内外单位。

我还在前面板上做了一个OLED显示屏的槽,并切了一小块铝片按尺寸贴在前面板上做装饰。

为了组装我用木材胶水和一些夹子以及一些螺钉。

我用喷漆给箱子上了漆。室外机用白漆,室内机用黑漆。在油漆干了之后,我简单地把pcb插入到箱子里。

在室内机的背面我插了一个电源插孔和一个电源开关,在室外机上我用了一根简单的跳线作为电源开关。

就是这样,我们的Arduino无线气象站现在正在工作,但是在这个视频中留下的是看看程序的工作原理。

Arduino无线气象站代码

Arduino气象站户外单位代码:

/* Arduino无线通信教程户外单元-发射器,Dejan Nedelkovski, www.HowToMechatronics.com图书馆:bet188官方网站NRF24L01 - TMRh20 / RF24 https://github.com/tmrh20/RF24/ DHT22 DHTlib, https://github.com/RobTillaart/Arduino/tree/master/libraries/DHTlib低电力——https://github.com/rocketscream/Low-Power * / # include < SPI.h > # include < nRF24L01.h > # include < RF24.h > # include < dht.h > # include < LowPower.h > # define dataPin 8 / / DHT22销dht dht的数据;//创建一个DHT对象RF24 radio(10, 9);// CE, CSN常量字节地址[6]= "00001";char thChar[32] = "";String thString = "";void setup() {radio.begin();radio.openWritingPipe(地址);radio.setPALevel (RF24_PA_MIN);radio.stopListening (); } void loop() { int readData = DHT.read22(dataPin); // Reads the data from the sensor int t = DHT.temperature; // Gets the values of the temperature int h = DHT.humidity; // Gets the values of the humidity thString = String(t) + String(h); thString.toCharArray(thChar, 12); // Sent the data wirelessly to the indoor unit for (int i = 0; i <= 3; i++) { // Send the data 3 times radio.write(&thChar, sizeof(thChar)); delay(50); } // Sleep for 2 minutes, 15*8 = 120s for (int sleepCounter = 15; sleepCounter > 0; sleepCounter--) { LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); } }

描述:室外单位是无线通信的发射机,所以这里首先需要包含RF24库, 这DHT图书馆,以及低功耗库它用于将Arduino放入睡眠模式。

在定义了它们的实例、模块连接的引脚和一些变量之后,我们需要在setup部分初始化无线通信地址。然后在循环部分,首先我们从DHT22传感器读取数据,即温度和湿度。最初这些值是整数,并且是分开的,所以我将它们转换为单个字符串变量,将它们放入字符数组中,并使用radio.write()函数将这些数据发送到室内单元。使用for循环,我们发送数据3次,以确保在发送时控制器很忙的情况下,接收者将获得数据。

最后,我们将Arduino置于一段特定时间的睡眠模式,以最小化功耗。

Arduino气象站室内单元代码:

/ * Arduino无线通信辅导室内单位 - 接收器由Dejan Nedelkovski,www.www.mfxpo.com图书馆:DS3231  -  http:/bet188官方网站/www.rinkydinkelectronics.com/library.php?id=73 U8G2必威lol  -  https://github.com/Olikraus / U8G2 * / #include  #include  #include  #include  #include  #include  #define Datapin 8 // DHT22传感器DHT DHT;//创建DHT对象DS3231 RTC(SDA,SCL);U8G2_SSD1306_128X64_NONAME_1_HW_I2C U8G2(U8G2_R0,/ * RESET = * / U8X8_PIN_NONE);RF24收音机(10,9);// CE, CSN常量字节地址[6]= "00001";char text [6] =“”;int readdht22,t,h;字符串Intemp,Inh​​um,OutTemp,Outhum;String RTCTIME,RTCDATE;int draw_state = 0; unsigned long previousMillis = 0; long interval = 3000; #define Temperature_20Icon_width 27 #define Temperature_20Icon_height 47 static const unsigned char Temperature_20Icon_bits[] U8X8_PROGMEM = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x80, 0x7f, 0x00, 0x00, 0xc0, 0xe1, 0x00, 0x00, 0xe0, 0xc0, 0x01, 0x00, 0x60, 0x80, 0xf9, 0x03, 0x60, 0x80, 0x01, 0x00, 0x60, 0x80, 0x01, 0x00, 0x60, 0x80, 0x79, 0x00, 0x60, 0x80, 0x01, 0x00, 0x60, 0x80, 0x01, 0x00, 0x60, 0x80, 0xf9, 0x03, 0x60, 0x80, 0x01, 0x00, 0x60, 0x80, 0x01, 0x00, 0x60, 0x8c, 0x79, 0x00, 0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0xf9, 0x03, 0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0x79, 0x00, 0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0xf9, 0x03, 0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0x01, 0x00, 0x60, 0x9e, 0x01, 0x00, 0x70, 0x9e, 0x03, 0x00, 0x38, 0x1e, 0x07, 0x00, 0x18, 0x3e, 0x0e, 0x00, 0x1c, 0x3f, 0x0c, 0x00, 0x0c, 0x7f, 0x18, 0x00, 0x8c, 0xff, 0x18, 0x00, 0x8e, 0xff, 0x38, 0x00, 0xc6, 0xff, 0x31, 0x00, 0xc6, 0xff, 0x31, 0x00, 0xc6, 0xff, 0x31, 0x00, 0x8e, 0xff, 0x38, 0x00, 0x8c, 0xff, 0x18, 0x00, 0x0c, 0x7f, 0x1c, 0x00, 0x3c, 0x1c, 0x0e, 0x00, 0x78, 0x00, 0x06, 0x00, 0xe0, 0x80, 0x07, 0x00, 0xe0, 0xff, 0x03, 0x00, 0x80, 0xff, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; #define Humidity_20Icon_width 27 #define Humidity_20Icon_height 47 static const unsigned char Humidity_20Icon_bits[] U8X8_PROGMEM = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, 0xdc, 0x01, 0x00, 0x00, 0x8e, 0x01, 0x00, 0x00, 0x86, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x03, 0x07, 0x00, 0x80, 0x03, 0x06, 0x00, 0x80, 0x01, 0x0c, 0x00, 0xc0, 0x01, 0x1c, 0x00, 0xc0, 0x00, 0x18, 0x00, 0xe0, 0x00, 0x38, 0x00, 0x60, 0x00, 0x30, 0x00, 0x70, 0x00, 0x70, 0x00, 0x30, 0x00, 0xe0, 0x00, 0x38, 0x00, 0xc0, 0x00, 0x18, 0x00, 0xc0, 0x01, 0x1c, 0x00, 0x80, 0x01, 0x0c, 0x00, 0x80, 0x03, 0x0e, 0x00, 0x80, 0x03, 0x06, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, 0x07, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x63, 0x00, 0x00, 0x06, 0x63, 0x00, 0x00, 0x06, 0x63, 0x00, 0x00, 0x06, 0xe3, 0x00, 0x00, 0x06, 0xc7, 0x00, 0x00, 0x06, 0xc6, 0x01, 0x00, 0x07, 0x86, 0x03, 0x00, 0x03, 0x0e, 0x1f, 0x00, 0x03, 0x0e, 0x1e, 0x80, 0x01, 0x1c, 0x00, 0xc0, 0x01, 0x38, 0x00, 0xe0, 0x00, 0x78, 0x00, 0x70, 0x00, 0xf0, 0x00, 0x38, 0x00, 0xe0, 0x07, 0x1f, 0x00, 0x80, 0xff, 0x0f, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 }; void setup() { radio.begin(); radio.openReadingPipe(0, address); radio.setPALevel(RF24_PA_MIN); radio.startListening(); u8g2.begin(); rtc.begin(); } void loop() { if (radio.available()) { radio.read(&text, sizeof(text)); // Read incoming data outTemp = String(text[0]) + String(text[1]) + char(176) + "C"; // Outdoor Temperature outHum = String(text[2]) + String(text[3]) + "%"; // Outdoor Humidity } unsigned long currentMillis = millis(); if (currentMillis - previousMillis > interval) { previousMillis = currentMillis; u8g2.firstPage(); do { switch (draw_state ) { case 0: drawDate(); break; case 1: drawInTemperature(); break; case 2: drawInHumidity(); break; case 3: drawOutTemperature(); break; case 4: drawOutHumidity(); break; } } while ( u8g2.nextPage() ); draw_state++; if (draw_state > 4) { draw_state = 0; } } } void drawDate() { String dowa = rtc.getDOWStr(); dowa.remove(3); rtcDate = dowa + " " + rtc.getDateStr(); u8g2.setFont(u8g2_font_timB14_tr); u8g2.setCursor(0, 15); rtcTime = rtc.getTimeStr(); // DS3231 RTC time rtcTime.remove(5); u8g2.print(rtcDate); u8g2.setFont(u8g2_font_fub30_tf); u8g2.setCursor(8, 58); u8g2.print(rtcTime); } void drawInTemperature() { readDHT22 = DHT.read22(dataPin); // Reads the data from the sensor t = DHT.temperature; // Gets the values of the temperature inTemp = String(t) + char(176) + "C"; u8g2.setFont(u8g2_font_helvR14_tr); u8g2.setCursor(24, 15); u8g2.print("INDOOR"); u8g2.setFont(u8g2_font_fub30_tf); u8g2.setCursor(36, 58); u8g2.print(inTemp); u8g2.drawXBMP( 0, 17, Temperature_20Icon_width, Temperature_20Icon_height, Temperature_20Icon_bits); } void drawInHumidity() { h = DHT.humidity; // Gets the values of the humidity inHum = String(h) + "%"; u8g2.setFont(u8g2_font_helvR14_tr); u8g2.setCursor(24, 15); u8g2.print("INDOOR"); u8g2.setFont(u8g2_font_fub30_tf); u8g2.setCursor(36, 58); u8g2.print(inHum); u8g2.drawXBMP( 0, 17, Humidity_20Icon_width, Humidity_20Icon_height, Humidity_20Icon_bits); } void drawOutTemperature() { u8g2.setFont(u8g2_font_helvR14_tr); u8g2.setCursor(12, 15); u8g2.print("OUTDOOR"); u8g2.setFont(u8g2_font_fub30_tf); u8g2.setCursor(36, 58); u8g2.print(outTemp); u8g2.drawXBMP( 0, 17, Temperature_20Icon_width, Temperature_20Icon_height, Temperature_20Icon_bits); } void drawOutHumidity() { u8g2.setFont(u8g2_font_helvR14_tr); u8g2.setCursor(12, 15); u8g2.print("OUTDOOR"); u8g2.setFont(u8g2_font_fub30_tf); u8g2.setCursor(36, 58); u8g2.print(outHum); u8g2.drawXBMP( 0, 17, Humidity_20Icon_width, Humidity_20Icon_height, Humidity_20Icon_bits); }

描述:另一方面,在室内单元或接收器上,我们需要增加两个图书馆,一个用于DS3231实时时钟模块还有一个是OLED显示屏U8G2图书馆。与以前一样,我们需要定义下面的程序的实例,引脚和一些变量。此外,我们还需要将温度和湿度图标定义为位图。

温度图标位图:

#define温度_20iCon_width 27 #define温度_20iCon_height 47静态const unsigned char温度_20iCon_bits [] u8x8_progmem = {0x00,0x00,0x00,0x00,0x00,0x3f,0x00,0x00,0x80,0x7f,0x00,0x00,0xc0,0xe1,0x00,0x00,0x00,0xe0,0xc0,0x01,0x00,0x60,0x80,0xf9,0x03,0x60,0x80,0x01,0x00,0x60,0x0,0x01,0x00,0x60,0x01,0x00,0x60,0x80,0x79,0x00,0x60,0x79,0x00,0x60,0x80,0x01,0x60,0x80,0x01,0x00,0x80,0x01,0x00,0x0,0x01,0x00,0x60,0x80,0x01,0x00,0x03,0x60,0x0,0x01,0x00,0x60,0x0,0x01,0x0,0x0,0x01,0x00,0x60,0x01,0x79,0x0,0x0,0x79,0x0,0x60,0x9e,0x01,0x60,0x9e,0x01,0x00,0x0,0x01,0x00,0x60,0x01,0x00,0x60,0x01,0x00,0x60,0x01,0x00,0x0,0x0,0x01,0x00,0x60,0x0,0xF9,0x03,0x60,0x9e,0x01,0x0,0x60,0x9e,0x01,0x00,0x60,0x0,0x79,0x0,0x0,0x79,0x01,0x60,0x9e,0x01,0x00,0x60,0x01,0x00,0x60,0x01,0x01,0x60,0x01,0x01,0x00,0x60,0x9e,0xf9,0x03,0x60,0x9e,0x01,0x00,0x60,0x0,0x01,0x00,0x60,0x0,0x01,0x00,0x70,0x01,0x00,0x70,0x9e,0x03,0x00,0x38,0x1e,0x07,0x00,0x18,0x3e,0x0e,0x00,0x1c,0x3f,0x0c,0x00,0x0c,0x7f,0x18,0x00,0x8c,0xff,0x18,0x00,0x0,0xff,0x38,0x00,0xC6,0xF,0x31,0x00,0xC6,0xff,0x31,0x00,0xc6,0xff,0x31,0x00,0x8e,0xff,0x38,0x00,0x8c,0xff,0x18,0x00,0x0c,0x7f,0x1c,0x00,0x3c,0x1c,0x0,0x3c,0x1c,0x0e,0x00,0x78,0x00,0x06,0x00,0x00,0xe0,0x80,0x07,0x00,0xe0,0xff,0x03,0x00,0x80,0xFF,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x00,0x00};

为此,我们可以使用g,这是一个开放源码的图像编辑器,通过它我们可以绘制任何东西,然后将其导出为位图(.xbm)。

然后我们可以用记事本打开这个文件,从那里我们可以复制位图到Arduino代码。

注意,这里我们可以使用programmem变量修饰符将位图定义为常量,这样位图就会存储在flash内存中,而不是Arduino板的SRAM中。

static const unsigned char Temperature_20Icon_bits[] u8x8_programmem // Save in the Flash memory static unsigned char Temperature_20Icon_bits[] // Save in the SRAM

在setup部分,我们需要初始化无线通信,以及初始化OLED显示器和实时时钟模块。

然后在循环部分中,我们不断检查是否有可通过NRF24L01模块读取的传入数据。如果为true,则使用radio.read()函数我们读取它,并将前两个字符存储到温度字符串变量中,并将下两个字符变为湿度串变量。

然后,我们使用millis()函数以interval变量定义的间隔(我将其设置为3秒)在显示器上显示各种数据。我们使用millis()函数是因为通过这种方式可以重复执行其余的代码,而如果使用delay()函数,程序将等待这段时间,因此我们可能会错过来自室外单元的传入数据。

接下来,使用U8G2库的firstPage()和nextPage()函数打印5个不同的屏幕,它们是用自定义函数定义的。

drawDate()自定义函数从实时时钟模块获取日期和时间信息,并将其适当地打印到显示器上。函数的作用是:读取室内温度并将其适当地打印在显示器上。事实上,同样的方法用于打印显示器上的所有屏幕。

所以这就是全部,我希望你喜欢这个Arduino项目并且学到了一些新的东西。请在下面的评论部分提出任何问题。

15反应

  1. 彼得年轻人

    我认为这将是一个伟大的想法,因为我一直想要自己的气象站,并决定尝试一下。
    我跟随你关于东西的电子方面的指示,并在我去的时候测试了每个部分。
    但是,在尝试将脚本上传到纳米或我的UNO时,它拒绝识别“DHT.H”即使我找到它,也可以将其放在与脚本相同的文件夹中。有没有其他人有这个问题,或者有没有办法解决它

    回复
    • 德国

      嘿,图书馆确实有问题。请确保下载并正确安装到Arduino库目录中。我可以注意到,我为GitHub上的库提供的链接现在没有下载按钮。你需要倒退两步,Arduino/libraries/DHTlib/。因此,在Arduino页面会出现下载按钮,因此下载所有库并将DHT库文件夹复制到Arduino库目录中。我希望这能行得通。

      回复
  2. Desie V-Zyl

    你好,伟大的教程!一个小问题,如果没有i2c,我将如何使用16×2 lcd而不是Oled?谢谢

    回复
      • Desie V-Zyl

        你能给我一个例子吗?我之前使用了16×2液晶,但不是NRF24L01,并且我非常困惑。它在Arduino项目列表上说,您可以使用此屏bet188me幕,以便我假设将提供适当的调整?

  3. Desie V-Zyl

    如何调整代码以使用16×2液晶屏?任何帮助,将不胜感激

    回复
  4. ernel.

    好日子先生
    我用的是dht11,这个设置可以吗?这么说,它拥有同样的图书馆?
    由于多

    回复
  5. Felix.

    你好,

    谢谢你的代码。除了室外温度和湿度不会改变之外,一切都很好。DHT22传感器正常工作。发送/接收数据是否正确存在问题?你知道代码中可能是什么吗?

    回复
    • 德国

      嗯,是的,我猜无线电通信是问题。尝试更简单的代码来测试通信是否正常工作,例如,尝试代码形成我的基本NRF24L01模块教程。

      回复
      • Felix.

        你好,

        问题是,它显示了外部的温度和湿度,但它只显示一次,如果外部的温度或湿度发生变化,它不会显示在显示器上。

        你知道我怎样才能解决这个问题吗?

  6. 吉姆

    你好,我目前正在研究这个项目,因为这将是我的小型工作台的完美补充,但我一直得到相同的错误代码。

    " Arduino: 1.8.7 (Windows 10), Board: " Arduino Nano, ATmega328P "
    Arduino_Wireless_Weather_Station_Indoor_Reciever:15:21:错误:U8g2lib.h:没有这样的文件或目录
    汇编终止。
    退出状态1.
    U8g2lib.h:没有这个文件或目录
    在C:\ Users \ Jim \ Documents \ Arduino \ Libraries \ U8G2中找到的无效库:在C:\ Users \ Jim \ Documents \ Arduino \ Libraries \ U8G2中找不到标题文件(.h)
    这份报告将会有更多的信息
    “在编译过程中显示详细输出”
    选项在文件中启用 - >首选项。

    我已经下载并加载了您在本教程中指定的U8G2库,但正如您所看到的,程序拒绝识别它,即使它已经正确安装在Libraries文件夹中。你有什么建议吗?我真的希望这能成功。谢谢你!

    回复
  7. 伊恩史密斯

    必须说,这个项目教会了我很多关于这些东西的新东西,我是一个退休的木工,所以建造盒子等对我来说很容易。下一步我了解了这个网站的多氯联苯很容易订购我的第一个多氯联苯了解了Gerber文件
    对你们这些新手来说,编程最大的问题是如果你们像我一样买便宜的arduino nano克隆产品到网上或youtube上看看如何设置,这是一个星期的拉头发,在屏幕上大喊,很多咒骂在youtube上找到一个视频5最常见的Arduino Nano克隆问题和他们的解决方案,这是一个伟大的视频为木制的头像我。所以,如果你像我一样,在这类事情上是全新的,坚持下去,你会在最后到达那里,非常感谢

    回复

留下一个回复

您的电子邮件地址不会被公开。

受到推崇的

2019年最佳入门级示波器为初学者和爱好者

为初学者和爱好者最好的示波器

受到推崇的

8个最好的Arduino入门工具包,适合2019年初学者

初学者的8个最佳Arduino Starter Kits

受到推崇的

最好的3D打印机为初学者和爱好者- 3D打印

最好的3D打印机为初学者和业余爱好者