Skip to content

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

weather00

Build

weather01 weather02

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