POEを搭載したイーサネットウェザーステーション(HTTP + Modbus)

良い一日Habrコミュニティ。

スマートホームに関する前回の投稿以来、多くの時間が経過しました。 気象観測所から始めようと決めました。





図1-プロトタイプの写真



arduinoの気象ステーションに関する記事が豊富にありますが(http://habrahabr.ru/post/165747/、habrahabr.ru/post/171525、habrahabr.ru/post/213405)、私はまだ決定を公表することにしました。



機能的




実行する機能:





実装できなかったが、私が欲しかった機能:



コスト〜1700rub(購入時のレートで)、含まれるもの:

  1. DHT22デジタル温度および湿度センサー$ 5.13
  2. GY-65 BMP085大気圧高度計モジュール$ 4.09
  3. GY-302 BH1750チップ光強度ライトモジュール$ 2.85
  4. POE / XbeeおよびSDカードスロット拡張を備えたArduino開発ボード用IBOARD W5100イーサネットモジュール$ 18.46
  5. MAX7219デジタルチューブディスプレイモジュール$ 4.74
  6. 40pcs 20cm 2.54mm 1p-1pピンデュポンワイヤケーブルラインラインコネクタ$ 2.42
  7. FT232RL USBシリアルラインダウンロードラインダウンローダーUSB to 232 $ 4.47
  8. プラスチックケース〜100こする




デバイスの実装


デバイスは、購入したArduinoベースのコンポーネントから完全に組み立てられました。 すべてのセンサーは配線され、購入した電気エールハウジング内に配置されました。 プロトタイプの透明な窓は透明な粘着テープでできています。



このデバイスには、低温でも問題なく動作するLEDディスプレイが搭載されており、コンピューターを使用せずに目的の目的でデバイスを使用できます。 夜間の使いやすさのために、照明のフィードバックはソフトウェアで整理されています。



Iboard w5100ボードは、多数のデバイスを接続する際に電源とグランドを増やすためのすべてのarduino +イーサネットシールド+センサーボード機能が既に含まれているという事実により、頭脳として選択されました。 このような高度に統合されたボードは、お金とスペースを節約します。 また、このボードはPassiv POEでの作業をサポートしています。



(ほとんどのスマートホームソリューションとは異なり)データ転送を有線にすることを決定した理由は次のとおりです。

  1. とにかくワイヤを引っ張ります-それが電源であろうとデータであろうと
  2. 化学電源からデバイスに電力を供給することが決定された場合、それらを交換する問題が発生します
  3. 有線通信はより信頼性が高いようです:

    1. Roskomnadzorからの苦情はありません。何も放射しないからです。
    2. 同じ周波数で他のトランシーバーの相互影響に問題はありません
    3. 無線通信の実装に関連する膨大な数の問題はありません(エーテルの一時的な分離、信号の減衰、受信ゾーンが重複する場合のその他のシステムの影響など)。
    4. Wifiで脳を温める人は穏やかです。


インターフェイスを作成せず、すべてを同じ方法で行うために、データ転送にイーサネットを使用することにしました。 Passiv POEテクノロジーを使用して、同じワイヤで電力を転送します。 この方法の利点-すべてのデバイスが共通のイーサネットネットワークに接続されている場合-プロトコルコンベクタ/ゲートウェイに疑問はありません。





図2-デバイスのブロック図





図3-接続図





図4-内面図



パッシブPOEの実装


パッシブPOE-これは、POEモジュールの特別な実装です。 元のPOEでは、プロトコルの実装が必要ですが、インジェクターは通信中にリモート機器の電力を決定し、電力を供給できる場合は記録します。 パッシブPOEは、そのようなトリッキーな実装を望まないcな中国人によって発明されましたが、POE機器市場への参入を望み、その結果、カテゴリ5Eケーブルの未使用のツイストペアケーブルに電圧を印加するための愚かな方法を思い付きました。 電気的特性の観点からIEEE 802.3af規格に準拠していると主張する人もいますが、そうであるとは限りません。



IBOARD W5100は、イーサネットコネクタの端子4.5および7.8に6〜20 Vの電圧を印加すると機能します。 20 Vを提供しています。





図5-POEインジェクションの接続図



インジェクターをD-LINK 320に取り付け、中国製のステップアップDC-DCコンバーターを取り付けました。





図6-写真の修正



インストール後-それは働いた。



注意! 電源を入れる前に、電源が20Vを出力するように設定されていることを確認してください。





図7-構造全体の動作



Webインターフェースの実装


Webインターフェースは、クライアント側で追加のソフトウェアを使用せずに、ネットワークからデバイスと最も便利な対話を行うために作成されました。 データへの自動アクセスの可能性については、XMLファイルの形式で情報を出力すると便利です。 このプロジェクトでは、XSLTプロセッサを使用してデータにアクセスするこれら2つの方法を組み合わせました。



Webブラウザーは192.168.0.20に要求を送信します。 応答のArduinoはXMLドキュメントを送信します。

<?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet type="text/xsl" href="http://192.168.0.20/z1.xsl"?> <response> <temperature> <celsius>30.70</celsius> <sensors> <sensor name='BMP' unit='C'>32.62</sensor> <sensor name='DHT' unit='C'>30.70</sensor> </sensors> </temperature> <humidity> <percentage>21.60</percentage> </humidity> <pressure> <pa>99309</pa> <mmHg>745</mmHg> </pressure> <illuminance> <lx>11</lx> </illuminance> </response>
      
      







この情報を美しい形で表示するために、ブラウザーはXSLTプロセッサーをロードし、それを使用してHTMLドキュメントを生成します。

ソースコード
 <?xml version='1.0' encoding='UTF-8'?> <xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'> <xsl:template match='/'> <html> <head> <title>Weather station</title> <meta http-equiv='refresh' content='5'/> <style> .z1 { font-family:Arial, Helvetica, sans-serif; color:#666; font-size:12px; text-shadow: 1px 1px 0px #fff; background:#eaebec; margin:20px; border:#ccc 1px solid; border-collapse:separate; border-radius:3px; box-shadow: 0 1px 2px #d1d1d1; } .z1 th { font-weight:bold; padding:15px; border-bottom:1px solid #e0e0e0; background: #ededed; background: linear-gradient(to top, #ededed, #ebebeb); } .z1 td { padding:10px; background: #f2f2f2; background: linear-gradient(to top, #f2f2f2, #f0f0f0); } .z1 tr:hover td{ background: #aaaaaa; background: linear-gradient(to top, #f2f2f2, #e0e0e0); } </style> </head> <body> <h2>Weather station</h2> <table class='z1'> <tr> <th>Property</th> <th>Value</th> </tr> <tr> <td> Temperature </td> <td><xsl:value-of select='response/temperature/celsius'/> C</td> </tr> <tr> <td> Humidity </td> <td><xsl:value-of select='response/humidity/percentage'/> %</td> </tr> <tr> <td> Pressure </td> <td><xsl:value-of select='response/pressure/mmHg'/> mm.Hg</td> </tr> <tr> <td> Illuminance </td> <td><xsl:value-of select='response/illuminance/lx'/> lx</td> </tr> </table> <h2>Termosensor</h2> <table class='z1'> <tr> <th>Sensor</th> <th>Value</th> </tr> <xsl:for-each select='response/temperature/sensors/sensor'> <tr> <td> <xsl:value-of select='@name'/> </td> <td><xsl:value-of select='.'/> <xsl:value-of select='@unit'/></td> </tr> </xsl:for-each> </table> </body> </html> </xsl:template> </xsl:stylesheet>
      
      









これらすべてのアクティビティの結果、WebブラウザーでのこのXMLドキュメントは次のようになります。



図8-FirefoxのWebベースのインターフェースの外観



ソフトウェア




ソフトウェアでは、プロセスを高速化するために既製のライブラリを最大限に活用しようとしました。



プログラムのロジックは非常に単純です-ライブラリから対応する関数を呼び出すことにより、すべてのデバイスを初期化します。



サイクルでは、必要に応じてすべてのセンサー測定値を読み取ります-8ビットLEDインジケーターのデータを更新し、イーサネット経由で要求を処理します。



プロジェクトでは次のライブラリが使用されました。





文字の出力については、LedControlライブラリの文字テーブルをアップグレードする必要がありました。 デフォルトでは、このライブラリは文字a〜fのみを表示できます。



ソースコード
 #include <Ethernet.h> #include <SPI.h> #include <Wire.h> #include "DHT.h" #include "BH1750.h" #include "Adafruit_BMP085.h" #include "LedControl.h" #include "Mudbus.h" #include "WebServer.h" #define WEATHER_STATION_Z1 0x20 // =============================================================== #define DHT_S1_PIN A0 //    DHT22 // =============================================================== // assign a MAC address for the ethernet controller. // fill in your address here: byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; // assign an IP address for the controller: IPAddress ip(192,168,0,20); IPAddress gateway(192,168,0,1); IPAddress subnet(255, 255, 255, 0); // =============================================================== float humidity = 0, temp_dht = 0, temp_bmp = 0, temp = 0; uint16_t light = 0; int32_t pressure_pa = 0, pressure_mm = 0; int mode = 0; dht dht_s1; BH1750 lightMeter; Adafruit_BMP085 bmp; /* This creates an instance of the webserver. By specifying a prefix * of "", all pages will be at the root of the server. */ #define PREFIX "" WebServer webserver(PREFIX, 80); //EthernetServer webserver(80); #define DEV_ID Mb.R[0] #define TEMPERATURE Mb.R[1] #define TEMPERATURE_DHT Mb.R[2] #define TEMPERATURE_BMP Mb.R[3] #define HUMIDITY Mb.R[4] #define PRESSURE_MM Mb.R[5] #define LIGHT Mb.R[6] Mudbus Mb; // pin A5 is connected to the DataIn // pin A6 is connected to the CLK // pin A7 is connected to LOAD LedControl lc=LedControl(A1,A2,A3,1); // ======================== Web pages ========================== void web_index(WebServer &server, WebServer::ConnectionType type, char *, bool) { /* this line sends the standard "we're all OK" headers back to the browser */ server.httpSuccess("application/xml; charset=utf-8"); /* if we're handling a GET or POST, we can output our data here. For a HEAD request, we just stop after outputting headers. */ if (type != WebServer::HEAD) { /* this defines some HTML text in read-only memory aka PROGMEM. * This is needed to avoid having the string copied to our limited * amount of RAM. */ P(index_p1) = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" "<?xml-stylesheet type=\"text/xsl\" href=\"http://192.168.0.20/z1.xsl\"?>" "<response>" " <temperature>" " <celsius>"; P(index_p2) = "</celsius>" " <sensors>" " <sensor name='BMP' unit='C'>"; P(index_p3) = "</sensor>" " <sensor name='DHT' unit='C'>"; P(index_p4) = "</sensor>" " </sensors>" " </temperature>" " <humidity>" " <percentage>"; P(index_p5) = "</percentage>" " </humidity>" " <pressure>" " <pa>"; P(index_p6) = "</pa>" " <mmHg>"; P(index_p7) = "</mmHg>" " </pressure>" " <illuminance>" " <lx>"; P(index_p8) = "</lx>" " </illuminance>" "</response>"; /* this is a special form of print that outputs from PROGMEM */ server.printP(index_p1); server.print(temp); server.printP(index_p2); server.print(temp_bmp); server.printP(index_p3); server.print(temp_dht); server.printP(index_p4); server.print(humidity); server.printP(index_p5); server.print(pressure_pa); server.printP(index_p6); server.print(pressure_mm); server.printP(index_p7); server.print(light); server.printP(index_p8); } } void web_z1_xsl(WebServer &server, WebServer::ConnectionType type, char *, bool) { server.httpSuccess("text/xsl; charset=utf-8"); if (type != WebServer::HEAD) { P(z1_xsl) = "<?xml version='1.0' encoding='UTF-8'?>" "<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>" "<xsl:template match='/'>" " <html>" " <head>" " <title>Weather station</title>" " <meta http-equiv='refresh' content='5'/>" " <style>" " .z1 {" " font-family:Arial, Helvetica, sans-serif;" " color:#666;" " font-size:12px;" " text-shadow: 1px 1px 0px #fff;" " background:#eaebec;" " margin:20px;" " border:#ccc 1px solid;" " border-collapse:separate; " " border-radius:3px;" " box-shadow: 0 1px 2px #d1d1d1;" " }" " .z1 th {" " font-weight:bold;" " padding:15px;" " border-bottom:1px solid #e0e0e0;" " background: #ededed;" " background: linear-gradient(to top, #ededed, #ebebeb);" " }" " .z1 td {" " padding:10px;" " background: #f2f2f2;" " background: linear-gradient(to top, #f2f2f2, #f0f0f0); " " }" " .z1 tr:hover td{" " background: #aaaaaa;" " background: linear-gradient(to top, #f2f2f2, #e0e0e0); " " }" " </style>" " </head>" " <body>" " <h2>Weather station</h2>" " <table class='z1'>" " <tr>" " <th>Property</th>" " <th>Value</th>" " </tr>" " <tr>" " <td> Temperature </td>" " <td><xsl:value-of select='response/temperature/celsius'/> C</td>" " </tr>" " <tr>" " <td> Humidity </td>" " <td><xsl:value-of select='response/humidity/percentage'/> %</td>" " </tr>" " <tr>" " <td> Pressure </td>" " <td><xsl:value-of select='response/pressure/mmHg'/> mm.Hg</td>" " </tr>" " <tr>" " <td> Illuminance </td>" " <td><xsl:value-of select='response/illuminance/lx'/> lx</td>" " </tr>" " </table>" " <h2>Termosensor</h2>" " <table class='z1'>" " <tr>" " <th>Sensor</th>" " <th>Value</th>" " </tr>" " <xsl:for-each select='response/temperature/sensors/sensor'>" " <tr>" " <td> <xsl:value-of select='@name'/> </td>" " <td><xsl:value-of select='.'/> <xsl:value-of select='@unit'/></td>" " </tr>" " </xsl:for-each>" " </table>" " </body>" " </html>" "</xsl:template>" "</xsl:stylesheet>"; /* this is a special form of print that outputs from PROGMEM */ server.printP(z1_xsl); } } // ===================================================== void setup(){ // Init LED display lc.shutdown(0,false); lc.setIntensity(0,2); lc.clearDisplay(0); lc.setChar(0,7,'L',false); lc.setChar(0,6,'O',false); lc.setChar(0,5,'A',false); lc.setChar(0,4,'d',false); // Ethernet SPI.begin(); Ethernet.begin(mac, ip); // Init Light sensor lightMeter.begin(); // Init pressure sensor if (!bmp.begin()) { Serial.println("ERROR: BMP085 sensor failed"); } //enable serial datada print Serial.begin(9600); Serial.println("Weather Z1 v 0.1"); //        webserver.setDefaultCommand(&web_index); webserver.addCommand("index.html", &web_index); webserver.addCommand("z1.xsl", &web_z1_xsl); webserver.begin(); } void loop(){ char buff[64]; int len = 64; mode = (mode + 1) % 100; Z1_sensors_update(); Z1_SerialOutput(); Z1_ledDisplay(); Z1_modbus_tcp_slave(); // Z1_http_server(); webserver.processConnection(buff, &len); } void Z1_sensors_update() { if (mode%30==0) { // BH1750 (light) light = lightMeter.readLightLevel(); // BMP085 (Temp and Pressure) temp_bmp = bmp.readTemperature(); pressure_pa = bmp.readPressure(); pressure_mm = pressure_pa/133.3; // DHT22 (Temp) if (dht_s1.read22(DHT_S1_PIN) == DHTLIB_OK) { humidity = dht_s1.humidity; temp_dht = dht_s1.temperature; temp = temp_dht; } else { temp = temp_bmp; } } } void Z1_SerialOutput() { Serial.print("T1= "); Serial.print(temp_dht); Serial.print(" *C \t"); Serial.print("T2= "); Serial.print(temp_bmp); Serial.print(" *C \t"); Serial.print("Pressure= "); Serial.print(pressure_mm); Serial.print(" mm \t"); Serial.print("Humidity= "); Serial.print(humidity); Serial.print(" %\t"); Serial.print("Light= "); Serial.print(light); Serial.print(" lx \t"); Serial.print("\n"); } void Z1_ledDisplay() { int v; if (light<50) { lc.setIntensity(0,0); } else if (light>80 && light<200) { lc.setIntensity(0,2); } else if (light>250 && light<1000) { lc.setIntensity(0,5); } else if (light>1100) { lc.setIntensity(0,15); } if (mode<=25) { // lc.clearDisplay(0); lc.setChar(0,7,'t',false); if (temp>=0) { lc.setChar(0,6,' ',false); } else { lc.setChar(0,6,'-',false); } v = (int)( temp / 10 ) % 10; lc.setDigit(0,5,(byte)v,false); v = (int)( temp ) % 10; lc.setDigit(0,4,(byte)v,true); v = (int)( temp * 10 ) % 10; lc.setDigit(0,3,(byte)v,false); lc.setChar(0,2,' ',false); lc.setChar(0,1,'*',false); lc.setChar(0,0,'C',false); delay(1); } else if (mode<=50) { // lc.clearDisplay(0); lc.setChar(0,7,'H',false); lc.setChar(0,6,' ',false); v = (int)( humidity / 10 ) % 10; lc.setDigit(0,5,(byte)v,false); v = (int)( humidity ) % 10; lc.setDigit(0,4,(byte)v,true); v = (int)( humidity * 10 ) % 10; lc.setDigit(0,3,(byte)v,false); lc.setChar(0,2,' ',false); lc.setChar(0,1,'*',false); lc.setChar(0,0,'o',false); delay(1); } else if (mode<=75) { // lc.clearDisplay(0); lc.setChar(0,7,'P',false); lc.setChar(0,6,' ',false); v = (int)( pressure_mm / 100 ) % 10; lc.setDigit(0,5,(byte)v,false); v = (int)( pressure_mm/10 ) % 10; lc.setDigit(0,4,(byte)v,false); v = (int)( pressure_mm ) % 10; lc.setDigit(0,3,(byte)v,false); lc.setChar(0,2,' ',false); lc.setChar(0,1,'n',false); lc.setChar(0,0,'n',false); delay(1); } else { // lc.clearDisplay(0); lc.setChar(0,7,'L',false); lc.setChar(0,6,' ',false); v = (int)( light / 1000 ) % 10; lc.setDigit(0,5,(byte)v,false); v = (int)( light / 100 ) % 10; lc.setDigit(0,4,(byte)v,false); v = (int)( light / 10 ) % 10; lc.setDigit(0,3,(byte)v,false); v = (int)( light ) % 10; lc.setDigit(0,2,(byte)v,false); lc.setChar(0,1,' ',false); lc.setChar(0,0,' ',false); delay(1); } } void Z1_modbus_tcp_slave() { Mb.Run(); DEV_ID = WEATHER_STATION_Z1; TEMPERATURE = temp*10; TEMPERATURE_DHT = temp_dht*10; TEMPERATURE_BMP = temp_bmp*10; HUMIDITY = humidity*10; PRESSURE_MM = pressure_mm; LIGHT = light; }
      
      









Arduinoコントローラーソフトウェアを使用したGitリポジトリ: github.com/krotos139/sh1_arduino_weather_station_v1



おわりに


イーサネットインターフェイスを備えたスマートウェザーステーションは、arduinoに簡単に実装できます。

パイロット生産時のデバイスのコスト-〜1700ルーブル

組立時間-〜1日

この機能は素晴らしいです-POEと標準のarduino電源から電力を供給されるスタンドアロンデバイスとして、またはスマートデバイスとして機能し、ブラウザから必要な情報をすべて取得できます。 自動化された情報処理の場合、デバイスはXMLドキュメントの形式でmodbusプロトコルを介して情報を提供します。



All Articles