Diverticula: a poor man's energy diverter (feat. OWL Intuition)

This was one of my abandoned project that I'm now reviving for the sake of sharing.
I work in the renewable energy field, and these are the days of self-consumption craze.
Some companies (take the Sussex based Power Diverter Ltd. or the Italian 4-noks) have PWM products that divert excess PV energy to an insert water heater.
These devices are quite expensive, and I always dreamed another way of doing it: a fixed step multi-stage insert heater.

As time went on I left this project for AUTOMA, which better suited my personal needs but since I recently saw at least one commercial product doing the exact same thing with the exact same trick, I decided to resurrect this.
I'm talking EGO Smart Heater, produced by the German E.G.O. GmbH and marketed as a Solar-Log accessory by Solare Datensysteme GmbH.

From the vaults then, please welcome Diverticula: a poor man's PV energy diverter for multi-stage insert heaters.

All you need is:

  • an Arduino Ethernet or equivalent assembly
  • a relay shield
  • an OWL Intuition -lc
  • a multi-stage insert heater

I used an OWL Intuition for the sake of convenience, in fact you can obtain electrical figures by any means you're comfortable with.
This Intuition dude saves your data on the cloud and multicasts its real time data onto your LAN, so I basically got all the dirty job done.
To add Multicast support to the Arduino Ethernet library, you'll need to manually add the beginMulti() method by Alasdair Allan.

As already explained in my AUTOMA project page, to discern a specific electrical load without too much hassle, Intuition comes really handy provided you apply a small undocumented hack to it.
The Intuition electrical unit (called CMR) has been designed with 3 barrel receptacles for 3 generic current clamps. The Intuition router (called Network OWL or ICU) then manages to treat those figures based upon your system configuration (3-phase, 1-phase + PV system, etc.).
In a single phase PV system configuration, the 3rd input channel is unused, but if you indeed insert another clamp connector there you'll find that its readings are summed up with the 1st clamp and treated as generic consumption.
So you have not 1 but 2 consumption channels that can be individually monitored.

Said so, you will need to buy an Intuition-lc (3 clamps) and wire it as if it was an Intuition-pv (2 clamps) with the additional clamp dedicated to the heater live wire.
If you already own a -pv, you can buy extra clamps for 10 bucks each, or so.
You may avoid a double measure simply sticking the heater neutral wire in the 1st clamp (or its live wire backwards).

Now to the insert heater.
It needs a very peculiar tech spec: it may have as many stages as you like (provided you have enough relays on your shield) but they need to be one the double of the other in terms of power consumption. 500+1000 watts is OK; 200+400+800 is fine; 650+1000+1500 is not.
The smallest figure will determine your granularity.
In fact they will be mapped and treated as single bits so a power of 2 is mandatory for the whole thing to work properly.

The sketch is as simple as it gets: it works fine but it lacks all the fault proof features of AUTOMA.
I'm talking Multicast timeout, power factor real time adjustment, support for multiple ICUs, consumption readings filtering, and of course minimum service level without fossil fuels.
If you're interested in such things, please have a look at the code of my AUTOMA project.
I also removed all SD logging and serial output messaging code, for you to see how clean the Arduino/Intuition integration looks like.

Note that I'm currently not involved with any of the companies linked here.
Since I don't write C++ code as my daily job, please be forgiving and feel free to suggest better coding practices.
A final advice: due to high energy involved in water heating, the direct connection without power contactors can and will smoke your relay board within seconds. In fact the rated max switching power printed on any surface mounted relay is far higher than its implementation onboard its PCB. Please refer to the tech specs of your relay board.

#include "SPI.h"
#include "Ethernet.h"

byte mac[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
const unsigned int base = 500;   //smallest heater stage wattage
const byte relays[] = {5, 6, 7}; //least to most powerful heater stage

const unsigned int PACKET_SIZE = 320;
const char ELECTRICITY_TAG[] = "<electricity";
const char SOLAR_TAG[] = "<solar";
const char ELECTRICITY_STR[] = "<chan id='0'><curr units='w'>";
const char SOLAR_STR[] = "<generating units='w'>";

const IPAddress mIP(224, 192, 32, 19); //Intuition multicast group
const unsigned int mPort = 22600;      //Intuition multicast port

EthernetUDP Multicast;
char buffer[PACKET_SIZE];
char *idx = buffer;

int prod;
int cons = 32767;
byte bits;
byte ceiling = pow(2, sizeof(relays))-1;

void setup () {
  for (byte i = 0; i < sizeof(relays); i++)
    pinMode(relays[i], OUTPUT);
  Ethernet.begin(mac);
  Multicast.beginMulti(mIP, mPort);
}

void loop() {
  if (Multicast.parsePacket()) {
    memset(buffer, 0, PACKET_SIZE);
    Multicast.read(buffer, PACKET_SIZE);
    if (received(SOLAR_TAG))
      prod = getValue(SOLAR_STR);
    else if (received(ELECTRICITY_TAG))
      cons = getValue(ELECTRICITY_STR);
    bits = constrain(float(prod-cons)/base, 0, ceiling);
    for (byte i = 0; i < sizeof(relays); i++)
      digitalWrite(relays[i], bits>>i&1);
  }
  delay(2000);
}

boolean received(const char* str) {
  idx = buffer;
  byte le = strlen(str);
  return strncmp(idx, str, le) == 0;
}

int getValue(const char* str) {
  idx = buffer;
  byte le = strlen(str);
  int value = 0;
  for (; idx[0] != NULL && strncmp(idx, str, le) != 0; idx++);
  for (byte i = 0; idx[0] != NULL && i < le; i++, idx++);
  for (; idx[0] != NULL && idx[0] >= '0' && idx[0] <= '9'; idx++)
    value = value * 10 + idx[0] - 48;
  return value;
}

I am confused. Are trying to sell something? Or are you trying to crowd-fund something?

Paul