Weather Station
This is version 1 of my DIY weather station.
Version 2 will incorporate air temperature, humidity and pressure which are currenly part of my separate solar powered temperature logger.
All data is fed to my Home Assistant instance running on an Intel NUC (more details to come).
Prototype
Build
Parts
My parts list was generally as follows (WIP):
Part | Description |
---|---|
ESP8266 | LOLIN D1 Mini Pro |
MODBUS RTL | MAX485 Module RS-485 TTL to RS485 MAX485CSA Converter Module |
Wind Vane | RS485 interface aluminum outdoor wind direction sensor SM5387B |
Anemometer | RS485 interface aluminum outdoor RS485 wind speed sensor SM5386B |
PSU | 1A 12W AC 220V to DC 12V PSU |
Arduino Code
I found this Wind speed transmitter user's Guide to be very useful to work out the modbus data format.
The manual wasn't for my exact model of equipment but it was near enough to work out the coding requirements (eventually).
Once I had this running a friend of mine (KMW) helped tidy the code structure. I now use this as my goto reference for other projects.
// KMW: #include files should generally be in sorted order, unless there's a reason
// otherwise. For C++, there might be a reason...
#include <ESP8266WiFi.h>
#include <ModbusMaster.h>
#include <PubSubClient.h>
#include <SoftwareSerial.h>
// Anemometer
#define WSPD_MAX485_DE 14
#define WSPD_MAX485_RE_NEG 12
#define WSPD_RX 13 // Serial Receive pin
#define WSPD_TX 16 // Serial Transmit pin
#define WSPD_TOPIC "sensor/w6_hillwind/speed"
// Wind Vane
#define WDIR_MAX485_DE 0
#define WDIR_MAX485_RE_NEG 4
#define WDIR_RX 5 // Serial Receive pin
#define WDIR_TX 2 // Serial Transmit pin
#define WDIR_TOPIC "sensor/w6_hillwind/bearing"
#include "secrets.h"
SoftwareSerial serialWDir(WDIR_RX, WDIR_TX);
SoftwareSerial serialWSpd(WSPD_RX, WSPD_TX);
ModbusMaster modbusWDir;
ModbusMaster modbusWSpd;
WiFiClient espClient;
PubSubClient mqtt_client(espClient);
//-------------------------------------------------------------------------
// KMW: I favour OTBS https://en.wikipedia.org/wiki/Indentation_style#1TBS
void enableTxWSpd () {
digitalWrite(WSPD_MAX485_RE_NEG, 1);
digitalWrite(WSPD_MAX485_DE, 1);
}
void disableTxWSpd () {
digitalWrite(WSPD_MAX485_RE_NEG, 0);
digitalWrite(WSPD_MAX485_DE, 0);
}
void enableTxWDir () {
digitalWrite(WDIR_MAX485_RE_NEG, 1);
digitalWrite(WDIR_MAX485_DE, 1);
}
void disableTxWDir () {
digitalWrite(WDIR_MAX485_RE_NEG, 0);
digitalWrite(WDIR_MAX485_DE, 0);
}
//-------------------------------------------------------------------------
void wifi_setup() {
delay(2000);
WiFi.hostname(hostname);
WiFi.begin(wifi_ssid, wifi_pass);
while (WL_CONNECTED != WiFi.status()) {
delay(500);
}
}
//-------------------------------------------------------------------------
void mqtt_reconnect() {
while (!mqtt_client.connected()) {
// KMW: The following if looks wrong - should it have a negation like "if (!mqtt_client.connect ..." ?
// KMW: The "delay" is also not in {}, so is it controlled by the "if"?
// KMW: Are we meant to be specifying our own name hostname in mqtt_client.connect, or the name of the mqtt broker?
if (mqtt_client.connect(hostname, mqtt_user, mqtt_pass));
delay(5000);
}
}
//-------------------------------------------------------------------------
void setup() {
// Anemometer
pinMode(WSPD_MAX485_RE_NEG, OUTPUT);
pinMode(WSPD_MAX485_DE, OUTPUT);
disableTxWSpd();
// Wind Vane
pinMode(WDIR_MAX485_RE_NEG, OUTPUT);
pinMode(WDIR_MAX485_DE, OUTPUT);
disableTxWDir();
wifi_setup();
mqtt_client.setServer(mqtt_server, mqtt_port);
serialWSpd.begin(9600);
serialWDir.begin(9600);
Serial.begin(9600);
modbusWSpd.begin(1, serialWSpd);
modbusWSpd.preTransmission( enableTxWSpd);
modbusWSpd.postTransmission(disableTxWSpd);
modbusWDir.begin(1, serialWDir);
modbusWDir.preTransmission( enableTxWDir);
modbusWDir.postTransmission(disableTxWDir);
}
//-------------------------------------------------------------------------
void loop() {
// KMW: This sample code runs "loop" even if it has just reconnected
// https://github.com/knolleary/pubsubclient/blob/master/examples/mqtt_esp8266/mqtt_esp8266.ino
if (!mqtt_client.connected()) {
mqtt_reconnect();
}
mqtt_client.loop();
// KMW: given that you're only publishing, and not doing anything else,
// there is no need to run mqtt_client.loop terribly often, but I'd still
// restructure it like the code sample mentioned above, where the main loop
// runs as a tight loop (no delays) and only does a publish when the elapsed
// time since last publish is long enough, like this:
//
// unsigned long now = millis();
// if (now - lastMsg > 2000) {
// lastMsg = now;
// ++value;
// snprintf (msg, MSG_BUFFER_SIZE, "hello world #%ld", value);
// Serial.print("Publish message: ");
// Serial.println(msg);
// client.publish("outTopic", msg);
// }
// Anemometer
uint8_t resultMain_a;
resultMain_a = modbusWSpd.readHoldingRegisters(0x0000, 1);
if (modbusWSpd.ku8MBSuccess == resultMain_a) {
float wspd = (modbusWSpd.getResponseBuffer(0x00) / 100.0f);
mqtt_client.publish(WSPD_TOPIC, String(wspd).c_str(), true);
}
delay(3000);
// Wind vane
uint8_t resultMain_d;
resultMain_d = modbusWDir.readHoldingRegisters(0x0000, 1);
if (modbusWDir.ku8MBSuccess == resultMain_d) {
float wdir = (modbusWDir.getResponseBuffer(0x00) / 100.0f);
mqtt_client.publish(WDIR_TOPIC, String(wdir).c_str(), true);
}
delay(5000);
}
//-------------------------------------------------------------------------
Last update: April 2, 2021