/*
(c) C.Epe, April 2022, use at your own risk!
Software Version 2.0:
- read status back from HMIP-MOD-RC8
- in setup function: trigger all "off"-Buttons of HMIP-MOD-RC8 on startup
- in loop function: check if wifi is still connected, if not "try to reconnect" (see checkWifi function)
- in loop function: check status of HMIP-MOD-RC8 after sending pv excess change, when sending failed =>resend it after 5000ms + wiggleLeds
- reduced pulse length for HMIP-MOD-RC8 to reduce the danger of max. duty cycle error
- implemented getTime-function, see "whatTimeisIt" to display time. Don´t forget to set timezone!
- all analogWrite(ledblue,_) are set to digitalWrite(ledblue,_), please set all resistors (R1, R2, R3) to 500-600 Ohm to get the LEDs darker.
- watchdog implemented for recognizing system crash
HMIP-PV-RC8
- This codes connects to your wifi. This is done in the setup function. Don´t forget your wifi SSID and passwort into arduino_secret.h
- Reading UDP data is based on example "WifiNINA\WiFiUdpSendReceiveString"
- Set localPort and IPAddress when you are using other energy meter than SMA Energymeter
- Reading UDP multicast parameters data is customized to SMA Energy Meter!
- Set timezone, updatetime, waiting & threshold under "necessary varables"
- Serial monitor in Arduino IDE show usefull information for debugging!
*/
#include <SPI.h>
#include <WiFiNINA.h>
#include <Adafruit_SleepyDog.h>
#include "additional_functions.h"
#include "arduino_secrets.h"
///////please enter your sensitive data in the Secret tab/arduino_secrets.h
char ssid[] = SECRET_SSID; // your network SSID (name)
char pass[] = SECRET_PASS; // your network password (use for WPA, or use as key for WEP)
int status = WL_IDLE_STATUS; // the WiFi radio's status
///////UDP variables & initialisation
unsigned int localPort = 502; // local port to listen on
IPAddress Energymeter = IPAddress(192,168,1,205); // ip address to listen to
char packetBuffer[600]; //buffer to hold incoming packet
char ReplyBuffer[] = "acknowledged"; // a string to send back
WiFiUDP Udp;
#define status_R 9 // Status Pin of HMIP-MOD-RC8 "Error" - Maybe future use
#define status_G 10 // Status Pin of HMIP-MOD-RC8 "Job done!" - Maybe future use
#define status_S 11 // Status Pin of HMIP-MOD-RC8 "Running activity" - Maybe future use
#define ledgreen A4 // Indicator led for pv excess
#define ledred A5 // Indicator led when there is no pv excess
#define ledblue A3 // Indicator led when HMIP-MOD-RC8 any of his inputs are set to status "on"
//Pins to HMIP-MOD-RC8
int DigiOut[9] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
//********************************************************************
// Necessary varables
static bool previous_state = false;
bool pvExcess = false;
static bool previous_pvExcess;
bool startcounter = false; // Set when counter2 should be active
bool state = false; // Used to switch on and off
bool flag = false; // indicate pv excess for more than waiting time
int counter = 0; // Counter for updating serial data
int counter2 = 0; // Counter for time to wait before changing pv excess indication
int updatetime = 1; // Updatetime for serial data, pv excess data update and waiting count
int waiting = 60; // wait x seconds before reporting HMIP-MOD-RC9 that pv excess is present. Used in LowPass()
unsigned long threshold = 0; // set this value (Watts). When pv excess is higher than this value for more than "waiting" seconds
// HMIP-MOD-RC8 will be turned on
bool hmipstate = false; //status for feedback coming from HMIP-MOD-RC8
uint8_t pulselength = 20;//pulse length of pulse send to HMIP-MOD-RC8 in Milliseconds
//set your time zone here, example: Europe/Berlin UTC +2
uint8_t timezone = 2;
//********************************************************************
void setup() {
// Initialize serial and wait for port to open:
Serial.begin(9600);
// Set up led pins
pinMode(ledgreen, OUTPUT);
pinMode(ledred, OUTPUT);
pinMode(ledblue, OUTPUT);
//Setup Inputs for status pin coming from HMIP-MOD-RC9
pinMode(status_R, INPUT);
pinMode(status_G, INPUT);
pinMode(status_S, INPUT);
// Set up DigiOuts to HMIP-MOD-RC8
for(int i=0; i<=8; i++){
pinMode(DigiOut[i], OUTPUT);
}
// check for the WiFi module:
if (WiFi.status() == WL_NO_MODULE) {
Serial.println("Communication with WiFi module failed!");
// don't continue
while (true);
}
String fv = WiFi.firmwareVersion();
if (fv < WIFI_FIRMWARE_LATEST_VERSION) {
Serial.println("Please upgrade the firmware");
}
// attempt to connect to WiFi network:
while (status != WL_CONNECTED) {
Serial.print("Attempting to connect to WPA SSID: ");
Serial.println(ssid);
status = WiFi.begin(ssid, pass);
//wiggle leds to indicate that HMIP-PV-RC8 is trying to connect to the wifi
wiggleLeds(ledred, ledgreen);
}
// you're connected now, so print out the data:
Serial.print("You're connected to the network");
printCurrentNet();
printWifiData();
Serial.print("\nStarting connection to server...");
// if you get a connection, report back via serial:
Udp.beginMulticast(Energymeter, localPort);
Serial.println(" connected udp multicast");
// start UDP service
getUDPdataSetup();
//uncomment if necessary
//printCurrentNet();
//write time text
Serial.print("The UTC time is "); // UTC is the time at Greenwich Meridian (GMT)
//trigger all "off"-Buttons of HMIP-MOD-RC8
digitalWrite(DigiOut[1], HIGH);
delay(pulselength);
digitalWrite(DigiOut[1], LOW);
delay(1200); //Wait for HMIP-MOD-RC8 status_S (100 ms) + status_G (400ms) + status_R (600ms)+buffer (100ms)
digitalWrite(DigiOut[3], HIGH);
delay(pulselength);
digitalWrite(DigiOut[3], LOW);
delay(1200); //Wait for HMIP-MOD-RC8 status_S (100 ms) + status_G (400ms) + status_R (600ms)+buffer (100ms)
digitalWrite(DigiOut[5], HIGH);
delay(pulselength);
digitalWrite(DigiOut[5], LOW);
delay(1200); //Wait for HMIP-MOD-RC8 status_S (100 ms) + status_G (400ms) + status_R (600ms)+buffer (100ms)
digitalWrite(DigiOut[7], HIGH);
delay(pulselength);
digitalWrite(DigiOut[7], LOW);
delay(1200); //Wait for HMIP-MOD-RC8 status_S (100 ms) + status_G (400ms) + status_R (600ms)+buffer (100ms)
//start watchdog
Watchdog.enable(10000); // if not reset within period in Milliseconds system will be restartet. Don´t go unter 10.000ms!
}
// start the loop
void loop() {
// reset watchdog
Watchdog.reset();
// check Wifi
checkWifi();
// check current hours
uint8_t hours = whatTimeIsIt(timezone);
//getUDPdata() reads multicast packet (600 Byte of data) and returns the amount of pv excess in "deci watt" 100 dW = 10 W
unsigned long Einspeisung = getUDPdata();
//only lowpass or change state when there is no error from HMIP-MOD-RC8
if (!digitalRead(status_R)){
//LowPass() takes "Einspeisung" and "low passes" the data. So quick changes of "Einspeisung" will be smoothed
state = LowPass(Einspeisung);
}
//update time set above!
delay(1000*updatetime);
// send to HMIP-MOD-RC8 only when state has changed. Example: Switching Channel 1 & 2.
if(previous_state!=state){
// light up blue led and switch HMIP-MOD-RC8
if(state){
digitalWrite(ledblue, HIGH);
hmipstate = false;
while (!hmipstate){
//Set impulse (50 ms duration) to DigiOut2 correspondig to "on"
digitalWrite(DigiOut[2], HIGH);
delay(pulselength);
digitalWrite(DigiOut[2], LOW);
delay(400); //wait for HMIP-MOD-RC8 sending
// if sending was successfull abort while loop, else re-send it.
if (digitalRead(status_G)){
hmipstate = !hmipstate;
Serial.println("HMIP-MOD-RC8 acknowledged!");
for (int t=0; t<=8; t++){
digitalWrite(ledblue, LOW);
delay(100);
digitalWrite(ledblue, HIGH);
delay(100);
}
}
//if sending to HMIP failed, reset watchdog and wait 5 seconds before resending.
else {
Watchdog.reset();
Serial.print("HMIP-MOD-RC8 not respondig or cannot send to Access Point!");
delay(3000);
Serial.println("Resending...");
delay(1000);
// indicate that we are here in this while-Loop
wiggleLeds(ledblue, ledgreen);
}
}
}
else{
digitalWrite(ledblue, LOW);
hmipstate = false;
while (!hmipstate){
//Set impulse (50 ms duration) to DigiOut1 correspondig to "off"
digitalWrite(DigiOut[1], HIGH);
delay(pulselength);
digitalWrite(DigiOut[1], LOW);
delay(400); //wait for HMIP-MOD-RC8 sending
// if sending was successfull abort while loop, else re-send it.
if (digitalRead(status_G)){
Serial.println("HMIP-MOD-RC8 acknowledged!");
hmipstate = !hmipstate;
for (int t=0; t<=8; t++){
digitalWrite(ledblue, HIGH);
delay(100);
digitalWrite(ledblue, LOW);
delay(100);
}
}
//if sending to HMIP failed, reset watchdog and wait 5 seconds before resending.
else {
Watchdog.reset();
Serial.print("HMIP-MOD-RC8 not respondig or cannot send to Access Point!");
delay(3000);
Serial.println("Resending...");
delay(1000);
// indicate that we are here in this while-Loop
wiggleLeds(ledblue, ledred);
}
}
}
}
// update state
previous_state=state;
}
// Lowpass function to prevent sudden change of "Einspeisung" status effecting HMIP
bool LowPass(unsigned long Einspeisung){
//If there is pv excess, set pvExcess and light up red green
//Einspeisung is returned in "deci watt" 100 dW = 10 W
if (Einspeisung/10>threshold){
pvExcess = true;
Serial.print("PV Excess! Use it :-)");
digitalWrite(ledred, LOW);
digitalWrite(ledgreen, HIGH);
//if status of pvExcess has changed start counter2, remember: function is called every "updatetime" seconds
if (previous_pvExcess != pvExcess){
Serial.println("****PV excess YES!");
startcounter = true;
previous_pvExcess = pvExcess;
Serial.print(previous_pvExcess);
}
}
//If there is no pv excess, set pvExcess to false and light up red led
else {
pvExcess = false;
digitalWrite(ledgreen, LOW);
digitalWrite(ledred, HIGH);
//if status of pvExcess has changed start counter2, remember: function is called every "updatetime" seconds
if (previous_pvExcess != pvExcess){
Serial.println("****Change to no PV excess!");
startcounter = true;
previous_pvExcess = pvExcess;
}
}
// if startcounter is true count up
if (startcounter){
counter2++;
// for Debbugging
//Serial.print("counter2:");
Serial.println(" ");
Serial.print(counter2); // times "updatetime" because function is called every "updatetime" seconds
Serial.print(" seconds since pv excess status change to ");
Serial.print(pvExcess);
Serial.print(" ");
}
//If there is no other change within waiting time update flag and tell loop function to set HMIP-MOD-RC8
if (counter2 >= waiting) {
if (pvExcess){
flag = true;
}
else{
flag = false;
}
counter2 = 0; // reset counter
startcounter = false; // stop counting
}
return flag;
}
//check wifi connection function
void checkWifi(){
//check if wifi is still connected
if (WiFi.status() != WL_CONNECTED){
//trigger all "off"-Buttons of HMIP-MOD-RC8
Serial.println("---------------Wifi connection lost!----------------");
Serial.println("-------Trigger off all HMIP-MOD-RC8 buttons---------");
digitalWrite(ledblue, LOW);
digitalWrite(DigiOut[1], HIGH);
delay(pulselength);
digitalWrite(DigiOut[1], LOW);
delay(1200); //Wait for HMIP-MOD-RC8 status_S (100 ms) + status_G (400ms) + status_R (600ms)+buffer (100ms)
digitalWrite(DigiOut[3], HIGH);
delay(pulselength);
digitalWrite(DigiOut[3], LOW);
delay(1200); //Wait for HMIP-MOD-RC8 status_S (100 ms) + status_G (400ms) + status_R (600ms)+buffer (100ms)
digitalWrite(DigiOut[5], HIGH);
delay(pulselength);
digitalWrite(DigiOut[5], LOW);
delay(1200); //Wait for HMIP-MOD-RC8 status_S (100 ms) + status_G (400ms) + status_R (600ms)+buffer (100ms)
digitalWrite(DigiOut[7], HIGH);
delay(pulselength);
digitalWrite(DigiOut[7], LOW);
delay(1200); //Wait for HMIP-MOD-RC8 status_S (100 ms) + status_G (400ms) + status_R (600ms)+buffer (100ms)
}
while (WiFi.status() != WL_CONNECTED) {
Serial.print("Attempting to connect to WPA SSID: ");
Serial.println(ssid);
status = WiFi.begin(ssid, pass);
//wiggle leds to indicate that HMIP-PV-RC8 is trying to connect to the wifi
if (WiFi.status() == WL_CONNECTED){
// you're connected now, so print out the data:
Serial.print("You're connected to the network");
printCurrentNet();
printWifiData();
//restart UDP service
Serial.print("\nStarting connection to UDP server...");
// if you get a connection, report back via serial:
Serial.println("\n connecting udp multicast");
Udp.beginMulticast(Energymeter, localPort);
Serial.println("\n connected udp multicast");
// start UDP service
getUDPdataSetup();
}
//wiggle leds to indicate that HMIP-PV-RC8 is trying to connect to the wifi
wiggleLeds(ledred, ledgreen);
}
}
unsigned long getUDPdata() {
unsigned long Einspeisung;
// if there's data available, read a packet
int packetSize = Udp.parsePacket();
if (packetSize) {
// read the packet into packetBufffer
int len = Udp.read(packetBuffer, 600);
//Serial.println(len);
if (len > 0) {
packetBuffer[len] = 0;
}
// See Packet information here:
// http://www.eb-systeme.de/?page_id=1240
// Someone has done a great job here!
//
Serial.println("***********************************");
//print momentaner Bezug
Serial.print("Bezug (Watt): ");
unsigned long highWordBezug = word(packetBuffer[32], packetBuffer[33]);
unsigned long lowWordBezug = word(packetBuffer[34], packetBuffer[35]);
unsigned long Bezug = highWordBezug << 16 | lowWordBezug;
Serial.print(Bezug/10); //Bezug is returned in "deci watt" 100 dW = 10 W
//print momentane Einspeisung
Serial.print(" Einspeisung (Watt): ");
unsigned long highWordEinspeisung = word(packetBuffer[52], packetBuffer[53]);
unsigned long lowWordEinspeisung = word(packetBuffer[54], packetBuffer[55]);
Einspeisung = highWordEinspeisung << 16 | lowWordEinspeisung;
Serial.print(Einspeisung/10); //Einspeisung is returned in "deci watt" 100 dW = 10 W
Serial.print(" ");
}
//Simulate pv excess for x seconds only for debugging - uncomment if you want to use it.
/*counter++;
Serial.print("Cyclecounter: ");
Serial.println(counter);
if (counter<=10){ // 10 seconds
Einspeisung = 5000;
}
else{
Einspeisung = 0;
}
if (counter>=40){
counter =0;
}
//Simulate pv excess end
*/
return Einspeisung; //Einspeisung is returned in "deci watt" 100 dW = 10 W
}
void getUDPdataSetup() {
// if there's data available, read a packet
int packetSize = Udp.parsePacket();
if (packetSize) {
Serial.print("Received packet of size ");
Serial.println(packetSize);
Serial.print("From ");
IPAddress remoteIp = Udp.remoteIP();
Serial.print(remoteIp);
Serial.print(", port ");
Serial.println(Udp.remotePort());
// read the packet into packetBufffer
int len = Udp.read(packetBuffer, 600);
//Serial.println(len);
if (len > 0) {
packetBuffer[len] = 0;
}
// See Packet information here:
// http://www.eb-systeme.de/?page_id=1240
// Someone has done a great job here!
//
Serial.print("***********************************");
Serial.println("Contents:");
//print vendor of device (SMA Energy Meter)
Serial.print("Vendor: ");
for (int i=0; i<=2;i++){
Serial.print(packetBuffer[i]);
}
Serial.println();
//print SUSyID of device
Serial.print("SUSyID: ");
unsigned long susyID = word(packetBuffer[20], packetBuffer[21]);
Serial.println(susyID);
//print serial number of device
Serial.print("Serial number: ");
unsigned long highWordSerial = word(packetBuffer[20], packetBuffer[21]);
unsigned long lowWordSerial = word(packetBuffer[22], packetBuffer[23]);
unsigned long serial = highWordSerial << 16 | lowWordSerial;
Serial.println(serial);
}
}
void printWifiData() {
// print your board's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
Serial.println(ip);
// print your MAC address:
byte mac[6];
WiFi.macAddress(mac);
Serial.print("MAC address: ");
printMacAddress(mac);
}
void printCurrentNet() {
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// print the MAC address of the router you're attached to:
byte bssid[6];
WiFi.BSSID(bssid);
Serial.print("BSSID: ");
printMacAddress(bssid);
// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.println(rssi);
// print the encryption type:
byte encryption = WiFi.encryptionType();
Serial.print("Encryption Type:");
Serial.println(encryption, HEX);
Serial.println();
}
void printMacAddress(byte mac[]) {
for (int i = 5; i >= 0; i--) {
if (mac[i] < 16) {
Serial.print("0");
}
Serial.print(mac[i], HEX);
if (i > 0) {
Serial.print(":");
}
}
Serial.println();
}