Weather station on Ethernet (HTTP + Modbus) powered by POE

Temperature measurement Moisture measurement Measurement of pressure Illumination measurement Indication of measured parameters Output of measured parameters via the HTTP interface in the form of an XML document HTTP XSLT processor output for styling XML when displayed by a browser Issuance of information via Modbus (I am going to use it as a smart home control protocol) Powered by Passive POE
ORIGINAL POST
By Yuri Yakovlev
components
Hardware Components
W5100 Ethernet Module
X 1
details

uccjan2.PNG

Good day, Habr community.
A lot of time has passed since my last post about smart home . I decided to do it starting from the weather station.


Figure 1 – Photo of a prototype

Despite the abundance of articles about weather stations on arduino (http://habrahabr.ru/post/165747/, habrahabr.ru/post/171525 , habrahabr.ru/post/213405 ) I still decided publish your solution.

Functional

The functions that it performs:

  • Temperature measurement
  • Moisture measurement
  • Measurement of pressure
  • Illumination measurement
  • Indication of measured parameters
  • Output of measured parameters via the HTTP interface in the form of an XML document
  • HTTP XSLT processor output for styling XML when displayed by a browser
  • Issuance of information via Modbus (I am going to use it as a smart home control protocol)
  • Powered by Passive POE


Functions that could not be implemented, but wanted:

  • Precipitation measurement
  • Direction of the wind
  • Wind speed
  • DHCP client

Cost ~ 1700 rubles (at the exchange rate at the time of purchase), it included:

  1. DHT22 Digital Temperature And Humidity Sensor $ 5.13
  2. GY-65 BMP085 Atmospheric Pressure Altimeter Module $ 4.09
  3. GY-302 BH1750 Chip Light Intensity Light Module $ 2.85
  4. IBOARD W5100 Ethernet Module for Arduino Development Board with POE / Xbee and SD Card Slot Expansion $ 18.46
  5. MAX7219 Digital Tube Display Module$ 4.74
  6. 40pcs 20cm 2.54mm 1p-1p pin Dupont wire cable Line connector $ 2.42
  7. FT232RL USB To Serial Line Download Line Downloader USB TO 232 $ 4.47
  8. Plastic case ~ 100 rubles

 

Implementation of the device

The device was completely assembled from commercial Arduino-based components. All the sensors were wired and located inside a purchased electrical appliance ale case. The transparent windows in the prototype are made of transparent adhesive tape.

The device has an LED display that will work well at low temperatures, and allows you to use the device for its intended purpose without a computer. For ease of use at night – the software has a feedback on illumination.

The Iboard w5100 board was chosen as the brain, due to the fact that it already contains all the functionality of arduino + Ethernet shield + sensor board for multiplying power and ground, when connecting a large number of devices. This highly integrated board will save money and space. This board also supports Passiv POE.

I decided to make data transfer wired (unlike most smart home solutions) for the following reasons:

  1. Wire pull anyway – be it power or data
  2. If it is decided to power the device from chemical power sources, then the question of their replacement will arise.
  3. Wired communications seem to be more reliable:
    1. There will be no claims from Roskomnadzor to them, since nothing is broadcast on the air
    2. There will be no problems with the mutual influence of other transceivers in the same frequency
    3. There will not be a huge number of problems associated with the implementation of radio communications (time division of the air, signal attenuation, the influence of other such systems when the reception areas overlap, etc.)
    4. People for whom Wifi warms up the brain will be calm.

In order not to produce interfaces, and to do everything uniformly, I decided to use Ethernet for data transmission. Power is transmitted over the same wire using Passiv POE technology. The advantages of this method are that if all devices are connected to a common Ethernet network, then there will be no issue with protocol convectors / gateways.


Figure 2 – Block diagram of the device


Figure 3 – Connection diagram


Figure 4 – Inside view

Passiv POE implementation

Passive POE – This is a special implementation of POE modules. In the original POE, the implementation of the protocol is required, while the injector, when establishing communication, determines the power of the remote equipment, and if it can power it, then it records. Passive POE was invented by cunning Chinese, who did not want to do such a cunning implementation, but wanted to enter the POE equipment market, and as a result, they came up with a stupid idea to supply voltage to unused twisted pairs of 5E cable. Some even write that they allegedly adhere to the IEEE 802.3af standard in terms of electrical characteristics, but even this is not always the case.

IBOARD W5100 works if a voltage of 6-20V is applied to pins 4,5 and 7,8 of the Ethernet connector. I supply 20 V.


Figure 5 – Connection diagram for POE injection

I installed the injector in the D-LINK 320, installing a Chinese step-up DC-DC converter into it.


Figure 6 – Photo of the modification

After installation – everything worked.

Attention! Before turning on, check that the power supply is set to supply 20V.


Figure 7 – Work of the whole structure

Implementation of the WEB interface

The web interface was made for the most convenient interaction with the device from the network without additional software on the client side. For the possibility of automated access to data, it would be convenient to issue information in the form of an XML file. In this project, these two methods of accessing data were combined using an XSLT processor.

The web browser sends a request for 192.168.0.20 ; The Arduino responds by sending an XML document:

<?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>

To display this information beautifully, the browser loads the XSLT processor and uses it to generate an HTML document.

Source

As a result of all these activities, this XML document in the Web browser looks like this:

Figure 8 – Appearance of the web interface in Firefox

Software

In software, I tried to use ready-made libraries to the maximum in order to speed up the process.

The logic of the program is quite simple – we initialize all devices by calling the corresponding functions from the libraries.

In the loop, we read all the sensor readings, if necessary, update the data on the 8-digit LED indicator and process the request via the Ethernet network.

The following libraries were used in the project:

  • DHTLib – Library of access to the temperature / humidity sensor
  • Adafruit-BMP085-Library – Pressure / Temperature Sensor Access Library
  • BH1750 – Light sensor access library
  • LedControl – Library for working with LED indicator for SPI protocol
  • Webduino – HTTP protocol implementation library
  • Mudbus – Modbus protocol implementation library

It is worth noting that in order to display letters, I had to modernize the symbol table in the LedControl library. By default, this library can only display the letters af.

Source

#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 controller software git repository: github.com/krotos139/sh1_arduino_weather_station_v1

Conclusion

Smart weather station with Ethernet interface can be easily implemented on arduino.
The cost price of the device in pilot production – ~ 1700 rubles Assembly
time – ~ 1 day The
functionality is fantastic – it can work as an independent device powered by POE and from a standard arduino power supply, as well as a smart device – allowing you to get all the necessary from the browser information. For automated information processing, the device provides information in the form of an XML document and via the modbus protocol.

uccjan2.PNG

Good day, Habr community.
A lot of time has passed since my last post about smart home . I decided to do it starting from the weather station.


Figure 1 – Photo of a prototype

Despite the abundance of articles about weather stations on arduino (http://habrahabr.ru/post/165747/, habrahabr.ru/post/171525 , habrahabr.ru/post/213405 ) I still decided publish your solution.

Functional

The functions that it performs:

  • Temperature measurement
  • Moisture measurement
  • Measurement of pressure
  • Illumination measurement
  • Indication of measured parameters
  • Output of measured parameters via the HTTP interface in the form of an XML document
  • HTTP XSLT processor output for styling XML when displayed by a browser
  • Issuance of information via Modbus (I am going to use it as a smart home control protocol)
  • Powered by Passive POE


Functions that could not be implemented, but wanted:

  • Precipitation measurement
  • Direction of the wind
  • Wind speed
  • DHCP client

Cost ~ 1700 rubles (at the exchange rate at the time of purchase), it included:

  1. DHT22 Digital Temperature And Humidity Sensor $ 5.13
  2. GY-65 BMP085 Atmospheric Pressure Altimeter Module $ 4.09
  3. GY-302 BH1750 Chip Light Intensity Light Module $ 2.85
  4. IBOARD W5100 Ethernet Module for Arduino Development Board with POE / Xbee and SD Card Slot Expansion $ 18.46
  5. MAX7219 Digital Tube Display Module$ 4.74
  6. 40pcs 20cm 2.54mm 1p-1p pin Dupont wire cable Line connector $ 2.42
  7. FT232RL USB To Serial Line Download Line Downloader USB TO 232 $ 4.47
  8. Plastic case ~ 100 rubles

 

Implementation of the device

The device was completely assembled from commercial Arduino-based components. All the sensors were wired and located inside a purchased electrical appliance ale case. The transparent windows in the prototype are made of transparent adhesive tape.

The device has an LED display that will work well at low temperatures, and allows you to use the device for its intended purpose without a computer. For ease of use at night – the software has a feedback on illumination.

The Iboard w5100 board was chosen as the brain, due to the fact that it already contains all the functionality of arduino + Ethernet shield + sensor board for multiplying power and ground, when connecting a large number of devices. This highly integrated board will save money and space. This board also supports Passiv POE.

I decided to make data transfer wired (unlike most smart home solutions) for the following reasons:

  1. Wire pull anyway – be it power or data
  2. If it is decided to power the device from chemical power sources, then the question of their replacement will arise.
  3. Wired communications seem to be more reliable:
    1. There will be no claims from Roskomnadzor to them, since nothing is broadcast on the air
    2. There will be no problems with the mutual influence of other transceivers in the same frequency
    3. There will not be a huge number of problems associated with the implementation of radio communications (time division of the air, signal attenuation, the influence of other such systems when the reception areas overlap, etc.)
    4. People for whom Wifi warms up the brain will be calm.

In order not to produce interfaces, and to do everything uniformly, I decided to use Ethernet for data transmission. Power is transmitted over the same wire using Passiv POE technology. The advantages of this method are that if all devices are connected to a common Ethernet network, then there will be no issue with protocol convectors / gateways.


Figure 2 – Block diagram of the device


Figure 3 – Connection diagram


Figure 4 – Inside view

Passiv POE implementation

Passive POE – This is a special implementation of POE modules. In the original POE, the implementation of the protocol is required, while the injector, when establishing communication, determines the power of the remote equipment, and if it can power it, then it records. Passive POE was invented by cunning Chinese, who did not want to do such a cunning implementation, but wanted to enter the POE equipment market, and as a result, they came up with a stupid idea to supply voltage to unused twisted pairs of 5E cable. Some even write that they allegedly adhere to the IEEE 802.3af standard in terms of electrical characteristics, but even this is not always the case.

IBOARD W5100 works if a voltage of 6-20V is applied to pins 4,5 and 7,8 of the Ethernet connector. I supply 20 V.


Figure 5 – Connection diagram for POE injection

I installed the injector in the D-LINK 320, installing a Chinese step-up DC-DC converter into it.


Figure 6 – Photo of the modification

After installation – everything worked.

Attention! Before turning on, check that the power supply is set to supply 20V.


Figure 7 – Work of the whole structure

Implementation of the WEB interface

The web interface was made for the most convenient interaction with the device from the network without additional software on the client side. For the possibility of automated access to data, it would be convenient to issue information in the form of an XML file. In this project, these two methods of accessing data were combined using an XSLT processor.

The web browser sends a request for 192.168.0.20 ; The Arduino responds by sending an XML document:

<?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>

To display this information beautifully, the browser loads the XSLT processor and uses it to generate an HTML document.

Source

As a result of all these activities, this XML document in the Web browser looks like this:

Figure 8 – Appearance of the web interface in Firefox

Software

In software, I tried to use ready-made libraries to the maximum in order to speed up the process.

The logic of the program is quite simple – we initialize all devices by calling the corresponding functions from the libraries.

In the loop, we read all the sensor readings, if necessary, update the data on the 8-digit LED indicator and process the request via the Ethernet network.

The following libraries were used in the project:

  • DHTLib – Library of access to the temperature / humidity sensor
  • Adafruit-BMP085-Library – Pressure / Temperature Sensor Access Library
  • BH1750 – Light sensor access library
  • LedControl – Library for working with LED indicator for SPI protocol
  • Webduino – HTTP protocol implementation library
  • Mudbus – Modbus protocol implementation library

It is worth noting that in order to display letters, I had to modernize the symbol table in the LedControl library. By default, this library can only display the letters af.

Source

#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 controller software git repository: github.com/krotos139/sh1_arduino_weather_station_v1

Conclusion

Smart weather station with Ethernet interface can be easily implemented on arduino.
The cost price of the device in pilot production – ~ 1700 rubles Assembly
time – ~ 1 day The
functionality is fantastic – it can work as an independent device powered by POE and from a standard arduino power supply, as well as a smart device – allowing you to get all the necessary from the browser information. For automated information processing, the device provides information in the form of an XML document and via the modbus protocol.

documents
Code
Git hub repo
Sample code

COMMENTS

Please Login to comment
  Subscribe  
Notify of
POSTED BY
Reusable S/W