Home Monitoring (home made) – Wattson

Reading when the hot water is heated by solar from the Wattson plug

Since the Wattson plug and the hot water tank are in close proximity, we can reuse the Arduino that reads the temperate to determine when the Wattson plug is on and heating the water.

Another light voltage converter is used for this as the Wattson plug has a flash sequence that can be used to determine whether it’s off or on.

When it’s on, the plug flashes:

on-state

But when it’s off, the plug flashes:

off-state

So we can write a bit of code that counts the number of times it’s in the “on” state vs the “off” state and determine from the count whether it was on or off for the time period. [Note there are many other ways that could be written…]

The resulting code for the hot water tank temperature and the Wattson plug state looks like:

#include <SPI.h>
#include <Ethernet.h>

///////// CHANGEABLE VALUES /////////

char pompeii[] = "192.168.0.16";
int pompeiiPort = 80;

double minutesBetweenCalls = 1.0;

const double temperatureOffset = 0.0;

const double temperatureMultiplier = 1.252;
const double temperatureCalculationOffset = 1.188;


///////// CHANGEABLE VALUES ABOVE /////////

EthernetClient pompeiiClient;
byte mac[] = {
  0x90, 0xA0, 0xDA, 0x0E, 0x9B, 0xE6};
char pompeiiService[] = "/pvoutput-post-temp.php";

long counter = 1L;
long cReadings = 0L;

const int temperatureSensorPin = A0;
const double analogueRange = 1024.0;
const double voltage = 5.0;
const double offset = 0.5;
const double milliVolts = 100.0;

// timing stuff
unsigned long lastTimeUploaded = millis();

unsigned long millisecondsPerMinute = 60000;
unsigned long minutesInHour = 60;
unsigned long timeBetweenCalls = minutesBetweenCalls * millisecondsPerMinute;


// taos sensor
const int taosSensorPin = A5;
unsigned long onCount = 0L;
unsigned long offCount = 0L;
unsigned int threshold = 500L;

void setup() {
  Serial.begin(9600);
  connectToEthernet();
}

void connectToEthernet()
{
  // attempt to connect to Wifi network:
  // start the Ethernet connection:
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP waiting 1 minute");
    delay(millisecondsPerMinute);

    if (Ethernet.begin(mac) == 0)
    {
      Serial.println("Failed to configure Ethernet using DHCP waiting 1 more minute");
      delay(millisecondsPerMinute);

      if (Ethernet.begin(mac) == 0) {
        Serial.println("Failed to configure Ethernet using DHCP stopping - will need reset");
        while(true);
      }
    }

  }
  // give the Ethernet shield a second to initialize:
  delay(1000);
  Serial.println("connecting...");

  Serial.print("Connected to the network IP: ");
  Serial.println(Ethernet.localIP());
}

void loop() {
  readTemperatureSensorValue();
  readImmersionPlugSensorValue();

  //getTimeFromPompeii();
  if (isTimeToUploadData())
  {
    Serial.println("Uploading data");
    sendResultsToPompeii();
    resetReadingsAfterUpload();
  }

  delay(1);
}

void readImmersionPlugSensorValue()
{
  int sensorVal = analogRead(taosSensorPin);
  //Serial.println(sensorVal);

  calculateOnOffStatus(sensorVal);
}

void resetImmersionPlugSensorCounts()
{
  onCount = 0L;
  offCount = 0L;
}

void calculateOnOffStatus(int sensorVal)
{
  if( sensorVal < threshold)
  {
    offCount++;
  }
  else
  {
    onCount++;
  }
}

boolean isTimeToUploadData() {
  unsigned long time = millis();

  if( (time - lastTimeUploaded) >= timeBetweenCalls) {
    Serial.println("Time to upload");
    lastTimeUploaded = time;
    return true;
  }
  return false;
}

int getPeriodImmersionOnOffStatus()
{
  Serial.print("On Count: ");
  Serial.print(onCount);
  Serial.print(" Off Count: ");
  Serial.println(offCount);

  if( onCount > offCount)
  {
    return 10;
  }
  else
  {
    return 0;
  }
}

/* Reads the temperature sensor */
void readTemperatureSensorValue() {
  int sensorVal = analogRead(temperatureSensorPin);
  cReadings = cReadings + sensorVal;
  counter = counter + 1L;
}

void resetReadingsAfterUpload()
{
  resetTemperatureSensorCounts();
  resetImmersionPlugSensorCounts();
}

void resetTemperatureSensorCounts()
{
  counter = 1L;
  cReadings = 0L;
}

double calculateAverageTemperatureOverPeriod()
{
  Serial.print("Sensor Value: ");
  Serial.print(averageSensorVal());

  // convert the ADCreading to voltage
  double voltageAv = (averageSensorVal()/analogueRange) * voltage;

  Serial.print(", Av Volts: ");

  Serial.print(voltageAv);
  Serial.print(", degrees C: ");
  double temperatureAv = (voltageAv - offset) * milliVolts;
  temperatureAv = ((temperatureMultiplier * (temperatureAv + temperatureOffset)) + temperatureCalculationOffset);
  Serial.println(temperatureAv);

  return temperatureAv;
}

double averageSensorVal()
{
  return (double)cReadings/(double)counter;
}

void sendResultsToPompeii() {
  Serial.println("sendResultsToPompeii");

  String postData = getPostData();
  Serial.println("post data: " + postData);

  if (pompeiiClient.connect(pompeii, pompeiiPort)) {
    Serial.println("connected to pompeii");
    // Make a HTTP request:
    pompeiiClient.print("POST ");
    pompeiiClient.print(pompeiiService);
    pompeiiClient.println(" HTTP/1.1");
    pompeiiClient.print("Host: ");
    pompeiiClient.print(pompeii);
    pompeiiClient.print(":");
    pompeiiClient.println(pompeiiPort);
    pompeiiClient.println("Accept: text/html");
    pompeiiClient.println("Content-Type: application/x-www-form-urlencoded; charset=UTF-8");
    pompeiiClient.print("Content-Length: ");
    pompeiiClient.println(postData.length());
    pompeiiClient.println("Pragma: no-cache");
    pompeiiClient.println("Cache-Control: no-cache");
    pompeiiClient.println("Connection: close");
    pompeiiClient.println();

    pompeiiClient.println(postData);
    pompeiiClient.println();

    pompeiiClient.stop();
    pompeiiClient.flush();
    Serial.println("Called pompeii");
  }
}

String getPostData()
{
  double averagedTemperature = calculateAverageTemperatureOverPeriod();
  Serial.print("temp to post is: ");
  Serial.println(averagedTemperature);

  char tempChar[10];
  dtostrf(averagedTemperature,3,2,tempChar);
  return "t=" + String(tempChar) + "&i=" + getPeriodImmersionOnOffStatus();
}

Next Part

Part 5: Aggregation of the data before uploading to PVOutput

Home Monitoring (home made) – Temperature

Reading the temperature of the hot water tank

This one is not as easy as the export meter as the meter cupboard and the hot water tank are not in the same area as one another, so we had to purchase another Arduino. This time instead of a wifi shield I got the Ethernet shield – in reflection, not too wise a choice as the cat6 (yes overkill) cable that runs to the cupboard took a collective 5 hours to install.

For the temperature sensor we bought a linear thermistor (for those interested in replicating this, you can use one of these: http://cpc.farnell.com/microchip/mcp9700a-e-to/thermistor-linear-10mv-c-to-92/dp/SC10459)

The temperature sensor is then pushed in through the insulating hot water tank jacket with a dob of conducting gel to help the sensor get as good a reading as possible.

(The program loaded on the Arduino is in the next part)

Next Part

Part 4: Reading when the hot water is heated by solar from the Wattson

Home Monitoring (home made) – Export

Reading the export meter

Reading the export meter is relatively easy as the Arduino in the meter cupboard can be extended to have another light voltage converter to read the export meter.

So we purchased another part from CPC (for those interested in replicating this, you can use one of these: http://cpc.farnell.com/taos/tsl257-lf/sensor-light-voltage-converter/dp/SC12392?Ntt=SC12392)

The Arduino program then needed extending to attach another interrupt for the export meter. The only gotcha here is that the export meter flashes 1000 times for 1kh, but the import meter only 800 times for 1kwh.

And it looks like this:

arduino-meter-reader

The complete Arduino program:

#include <SPI.h>
#include <WiFi.h>
#include <WiFiUDP.h>

///////// CHANGEABLE VALUES /////////

double importMultiplier = 1.25;
double exportMultiplier = 1.0;

char ssid[] = "REPLACE THIS";
char pass[] = "REPLACE THIS";

char pompeii[] = "192.168.0.16";
int pompeiiPort = 80;

double minutesBetweenCalls = 1.0;

///////// CHANGEABLE VALUES ABOVE /////////

int status = WL_IDLE_STATUS;

WiFiClient pompeiiClient;
char pompeiiService[] = "/pvoutput-post.php";

unsigned long importImpulseCount = 0;
unsigned long exportImpulseCount = 0;

unsigned long lastTimeUploaded = millis();

boolean importInterruptAttached = false;
boolean exportInterruptAttached = false;

double millisecondsPerMinute = 60000.0;
double timeBetweenCalls = minutesBetweenCalls * millisecondsPerMinute;

void setup() {
  Serial.begin(9600);
  connectToWiFi();
}

void connectToWiFi()
{
  // attempt to connect to Wifi network:
  while ( status != WL_CONNECTED) {
    Serial.print("Attempting to connect to WPA SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network:    
    status = WiFi.begin(ssid, pass);

    delay(5000);
  }

  Serial.print("Connected to the network");
  printWifiData();
}

void loop() {
  addImportFlashInterrupt();
  addExportFlashInterrupt();

  if (isTimeToUploadData())
  {
    Serial.println("Uploading data");
    sendResultsToPompeii();
  }
}

void addImportFlashInterrupt()
{
  if (!importInterruptAttached)
  {
    importInterruptAttached = true;
    Serial.println("Attaching import interrupt on pin 3");
    attachInterrupt(1, flashImport, FALLING);
  }
}

void addExportFlashInterrupt()
{
  if (!exportInterruptAttached)
  {
    exportInterruptAttached = true;
    Serial.println("Attaching export interrupt on pin 2");
    attachInterrupt(0, flashExport, FALLING);
  }
}

boolean isTimeToUploadData() {
  unsigned long time = millis();

  if( (time - lastTimeUploaded) >= timeBetweenCalls) {
    Serial.println("Time to upload");
    lastTimeUploaded = time;
    return true;
  }
  return false;
}


void printWifiData() {
  // print your WiFi shield's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);
}

/* Handles the interrupt flash logic */
void flashImport() {
  importImpulseCount++;
}

void flashExport() {
  exportImpulseCount++;
}

void sendResultsToPompeii() {
  Serial.println("sendResultsToPompeii");

  String postData = getPostData();
  Serial.println(postData);

  if (pompeiiClient.connect(pompeii, pompeiiPort)) {
    Serial.println("connected to pompeii");
    // Make a HTTP request:
    pompeiiClient.print("POST ");
    pompeiiClient.print(pompeiiService);
    pompeiiClient.println(" HTTP/1.1");
    pompeiiClient.print("Host: ");
    pompeiiClient.print(pompeii);
    pompeiiClient.print(":");
    pompeiiClient.println(pompeiiPort);
    pompeiiClient.println("Accept: text/html");
    pompeiiClient.println("Content-Type: application/x-www-form-urlencoded; charset=UTF-8");
    pompeiiClient.print("Content-Length: ");
    pompeiiClient.println(postData.length());
    pompeiiClient.println("Pragma: no-cache");
    pompeiiClient.println("Cache-Control: no-cache");
    pompeiiClient.println("Connection: close");
    pompeiiClient.println();

    pompeiiClient.println(postData);
    pompeiiClient.println();

    pompeiiClient.stop();
    pompeiiClient.flush();
    Serial.println("Called pompeii");
  }
}

String calculateImportWattsAndResetFlashes()
{
  if (importImpulseCount == 0UL) {
    return "0";
  }

  double watts = (importImpulseCount * importMultiplier);
  Serial.print("Calculated Import Watts: ");
  Serial.println(watts);
  
  importImpulseCount = 0UL;
  
  return doubleToString(watts);
}

String calculateExportWattsAndResetFlashes()
{
  if (exportImpulseCount == 0UL) {
    return "0";
  }

  double watts = (exportImpulseCount * exportMultiplier);
  Serial.print("Calculated Export Watts: ");
  Serial.println(watts);
  
  exportImpulseCount = 0UL;
  
  return doubleToString(watts);
}

String doubleToString(double valueToConvert)
{
  char doubleChar[15];
  dtostrf(valueToConvert,9,2,doubleChar);
  String dts = String(doubleChar);
  Serial.println(dts);
  return dts;
}

String getPostData()
{
  String importWatts = calculateImportWattsAndResetFlashes();
  String exportWatts = calculateExportWattsAndResetFlashes();
  
  String wattHourPeriod = doubleToString(timeBetweenCalls);
  
  return "iw=" + importWatts + "&ew=" + exportWatts + "&msBetweenCalls=" + wattHourPeriod;
}

Next Part

Part 3: Reading the temperature of the hot water tank