You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
284 lines
11 KiB
284 lines
11 KiB
/**********************************************************************************/
|
|
/* */
|
|
/* MQTT_energymeter counts light pulses from an energy meter, converts it to Wh */
|
|
/* and publishes it to an MQTT broker via ethernet. Every 1000Wh the counter is */
|
|
/* saved to EEPROM. Every power up this value is read from EERPOM as the starting */
|
|
/* value for the counter, preventing data loss after a power outage. */
|
|
/* */
|
|
/* (C)2022 M.T. Konstapel https://meezenest.nl/mees */
|
|
/* */
|
|
/* This file is part of MQTT_energymeter. */
|
|
/* */
|
|
/* MQTT_energymeter is free software: you can redistribute it and/or modify */
|
|
/* it under the terms of the GNU General Public License as published by */
|
|
/* the Free Software Foundation, either version 3 of the License, or */
|
|
/* (at your option) any later version. */
|
|
/* */
|
|
/* MQTT_energymeter is distributed in the hope that it will be useful, */
|
|
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
|
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
|
/* GNU General Public License for more details. */
|
|
/* */
|
|
/* You should have received a copy of the GNU General Public License */
|
|
/* along with MQTT_energymeter. If not, see <https://www.gnu.org/licenses/>. */
|
|
/* */
|
|
/**********************************************************************************/
|
|
|
|
#include <UIPEthernet.h>
|
|
// The connection_data struct needs to be defined in an external file.
|
|
#include <UIPServer.h>
|
|
#include <UIPClient.h>
|
|
//#include "utility/logging.h"
|
|
#include "PubSubClient.h"
|
|
#include <EEPROM.h> //Internal EEPROM
|
|
|
|
#define MACADDRESS 0x12,0x7C,0x54,0x33,0xB4,0xA2
|
|
#define MYIPADDR 192,168,88,120
|
|
#define MYIPMASK 255,255,255,0
|
|
#define MYDNS 192,168,88,1
|
|
#define MYGW 192,168,88,1
|
|
#define LISTENPORT 1000
|
|
#define UARTBAUD 115200
|
|
|
|
/* CHANGE THESE VALUES TO CUSTOMIZE DATA LOGGER */
|
|
#define CLIENT_ID "MeesElectronics" // Our MQTT client ID
|
|
#define CLIENT_TOPIC "energy/solar" // Name of the topic we publish
|
|
//IPAddress IP_MQTT_broker(192, 168, 89, 10); // IP address of the MQTT broker
|
|
//String string_ip_mqtt_broker = "192.168.88.10";
|
|
char IP_string_MQTT_broker[20] = "192.168.88.11";
|
|
uint16_t MQTT_port = 1884;
|
|
IPAddress IP_MQTT_broker;
|
|
|
|
#define PULSES_PER_KWH 500 // 500 pulses from the energy meter is one kWh
|
|
/* END USER DEFINABLE VALUES */
|
|
|
|
uint8_t mac[6] = {MACADDRESS};
|
|
uint8_t myIP[4] = {MYIPADDR};
|
|
uint8_t myMASK[4] = {MYIPMASK};
|
|
uint8_t myDNS[4] = {MYDNS};
|
|
uint8_t myGW[4] = {MYGW};
|
|
|
|
EthernetServer server = EthernetServer(LISTENPORT);
|
|
EthernetClient ethClient;
|
|
PubSubClient mqttClient;
|
|
|
|
#define PULSES_PER_WH 1000/PULSES_PER_KWH // Calculates how many pulses corresponds to a Wh
|
|
#define INTERVAL 10000 // 10 sec delay between MQTT publishings
|
|
long previousMillis;
|
|
|
|
char numberArray[20];
|
|
uint32_t PulseCount=0; // This variable holds the pulse count from the energy meter
|
|
uint32_t EnergyReading=0; // This variable holds the number of Watt-hours
|
|
uint32_t stored_kwh_count=0; // Counted whole kWh value sored in EEPROM
|
|
|
|
void onDetectInterrupt()
|
|
{
|
|
// The Arduino calls this function when it detects a falling edge on pin 2.
|
|
// Received a pulse from the energy meter: add one to the counter
|
|
PulseCount++;
|
|
}
|
|
|
|
void setup() {
|
|
// setup serial communication
|
|
Serial.begin(UARTBAUD);
|
|
Serial.println(F("Energy meter with MQTT client."));
|
|
|
|
// Read EEPROM address 0. If it is 0xFF the EEPROM is probaly empty and we set EEPROM address 0 to 0x5A
|
|
if (EEPROM.read(0) == 0xFF) {
|
|
Serial.println(F("Powered up for the first time. Saving the default settings to the EEPROM."));
|
|
EEPROM.write(0, 0x5A);
|
|
EEPROM.put(1, stored_kwh_count); // Save zero to EEPROM
|
|
EEPROM.put(5, MQTT_port); // Save MQTT broker port
|
|
EEPROM.put(9,IP_string_MQTT_broker); //Save default IP address of MQTT broker
|
|
}
|
|
// Now we test if the first address of the EEPROM is 0x5A. If so, te EEPROM is correctly initialized. We can assume that the stored
|
|
// stored_kwh_count at address 1-4 is valid.
|
|
if (EEPROM.read(0) == 0x5A) {
|
|
Serial.print(F("Valid configuration found in EEPROM.\nLast stored kWh value: "));
|
|
EEPROM.get(1,stored_kwh_count);
|
|
Serial.println(stored_kwh_count);
|
|
EEPROM.get(9,IP_string_MQTT_broker); //Save default IP address of MQTT broker
|
|
Serial.print(F("IP of MQTT broker: "));
|
|
Serial.println(IP_string_MQTT_broker);
|
|
EEPROM.get(5, MQTT_port);
|
|
Serial.print(F("Port of MQTT broker: "));
|
|
Serial.println(MQTT_port);
|
|
|
|
} else {
|
|
// There was a problem reading the EEPROM. Try resetting the EEPROM settings and halt. A reboot of the device might solve the issue.
|
|
Serial.println(F("Error reading the EEPROM. Try rebooting the device."));
|
|
EEPROM.write(0, 0x5A);
|
|
EEPROM.put(1, stored_kwh_count); // Save zero to EEPROM
|
|
EEPROM.put(5, MQTT_port); // Save MQTT broker port
|
|
EEPROM.put(9,IP_string_MQTT_broker); //Save default IP address of MQTT broker
|
|
while(1);
|
|
}
|
|
|
|
// Attach interrupt to input pin connected to pulse input
|
|
pinMode(2, INPUT_PULLUP);
|
|
attachInterrupt(digitalPinToInterrupt(2), onDetectInterrupt, FALLING);
|
|
|
|
// initialize the ethernet device
|
|
//Ethernet.begin(mac,myIP,myDNS,myGW,myMASK);
|
|
Serial.print(F("Configuring ethernet..."));
|
|
//Ethernet.begin(mac,myIP,myDNS,myGW,myMASK);
|
|
// setup ethernet communication using DHCP
|
|
if (Ethernet.begin(mac) == 0) {
|
|
Serial.println("[FAIL]");
|
|
while(1);
|
|
};
|
|
Serial.println(F("[OK]"));
|
|
// start listening for clients
|
|
server.begin();
|
|
|
|
// setup mqtt client
|
|
IP_MQTT_broker.fromString(IP_string_MQTT_broker);
|
|
mqttClient.setClient(ethClient);
|
|
mqttClient.setServer(IP_MQTT_broker,MQTT_port);
|
|
Serial.println(F("MQTT client configured"));
|
|
|
|
previousMillis = millis();
|
|
}
|
|
|
|
void loop() {
|
|
size_t size;
|
|
uint16_t cnt;
|
|
char tmp_string[6];
|
|
|
|
// Telnet loop
|
|
if (EthernetClient client = server.available())
|
|
{
|
|
if (client)
|
|
{
|
|
while((size = client.available()) > 0)
|
|
{
|
|
uint8_t* msg = (uint8_t*)malloc(size+1); //make memory allocation one byte larger to accomodate for the NULL string terminator.
|
|
size = client.read(msg,size);
|
|
|
|
msg[size-1] = 0; //Add NULL to terminate the string
|
|
|
|
// Set IP of MQTT broker
|
|
if (msg[0] == ':' && size>9 && size<20)
|
|
{
|
|
// Extract IP address from command
|
|
for (cnt=2; cnt < (size-2); cnt++)
|
|
{
|
|
IP_string_MQTT_broker[cnt-2] = msg[cnt];
|
|
}
|
|
IP_string_MQTT_broker[cnt-2] = 0; // Add NULL to string
|
|
|
|
if (IP_MQTT_broker.fromString((char*)IP_string_MQTT_broker))
|
|
{
|
|
client.write("MQTT server set to ",19);
|
|
Serial.print(F("MQTT server set to : "));
|
|
Serial.println(IP_string_MQTT_broker);
|
|
client.write(msg,size);
|
|
}
|
|
else
|
|
{
|
|
client.write("?",1);
|
|
Serial.println(F("?"));
|
|
}
|
|
}
|
|
// Set port of MQTT broker
|
|
else if (msg[0] == 'P' && size>3 && size<10)
|
|
{
|
|
// Clear temporary string
|
|
for (cnt=0; cnt < sizeof(tmp_string); cnt++)
|
|
tmp_string[cnt]=0;
|
|
|
|
// Extract port number from command
|
|
uint16_t port=0;
|
|
for (cnt=2; cnt < (size-2); cnt++)
|
|
{
|
|
if (msg[cnt]>=48 && msg[cnt]<=57)
|
|
{
|
|
port = 10*port + (msg[cnt]-48);
|
|
tmp_string[cnt-2] = msg[cnt];
|
|
}
|
|
}
|
|
tmp_string[cnt-2] = 0; // Add NULL to string
|
|
|
|
if (port>0 && port<=0xFFFF)
|
|
{
|
|
MQTT_port=port;
|
|
client.write("MQTT port set to ",17);
|
|
Serial.print(F("Set port : "));
|
|
Serial.println(MQTT_port);
|
|
client.write(tmp_string,cnt-2);
|
|
}
|
|
else
|
|
{
|
|
client.write("?",1);
|
|
Serial.println(F("?"));
|
|
}
|
|
}
|
|
// Print IP and port of MQTT broker
|
|
else if (msg[0] == 'I')
|
|
{
|
|
client.write("MQTT server set to : ",21);
|
|
Serial.print(F("MQTT server set to : "));
|
|
Serial.print(IP_string_MQTT_broker);
|
|
Serial.print(F(" port : "));
|
|
Serial.println(MQTT_port);
|
|
client.write(IP_string_MQTT_broker,sizeof(IP_string_MQTT_broker));
|
|
client.write(" port : ",8);
|
|
itoa(MQTT_port,tmp_string,10); //(integer, yourBuffer, base)
|
|
client.write(tmp_string,sizeof(tmp_string));
|
|
|
|
}
|
|
// Save IP of MQTT broker to EEPROM
|
|
else if (msg[0] == 'S')
|
|
{
|
|
EEPROM.put(5, MQTT_port); //Save port address of MQTT broker
|
|
EEPROM.put(9,IP_string_MQTT_broker); //Save IP address of MQTT broker
|
|
client.write("SAVED",5);
|
|
Serial.println(F("SAVED"));
|
|
|
|
}
|
|
// Unknown command
|
|
else
|
|
{
|
|
client.write("?",1);
|
|
Serial.println(F("?"));
|
|
}
|
|
client.write("\n",1);
|
|
|
|
free(msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
// check interval
|
|
if(millis() - previousMillis > INTERVAL)
|
|
{
|
|
sendData();
|
|
Serial.print(F(CLIENT_TOPIC));Serial.print(F(" : "));Serial.println(EnergyReading);
|
|
previousMillis = millis();
|
|
}
|
|
mqttClient.loop();
|
|
readEnergymeter();
|
|
}
|
|
|
|
void sendData() {
|
|
if(mqttClient.connect(CLIENT_ID)) {
|
|
//Convert unsigned long to string (base 10) and send it to our MQTT broker
|
|
mqttClient.publish(CLIENT_TOPIC, ultoa(EnergyReading, numberArray, 10));
|
|
}
|
|
}
|
|
|
|
// Counts pulses from energy meter. Every pulse is equivalent to a certain amount of energy in Wh. This is almost always stated on te front of the meter.
|
|
// With variable PULSES_PER_KWH you can define this value.
|
|
// Every PULSES_PER_KWH pulses is saved to EEPROM. After a powercycle this value is read from EEPROM.
|
|
void readEnergymeter(){
|
|
// Every kWh (PULSES_PER_KWH) we increment and save kWh value to EEPROM
|
|
if (PulseCount >= PULSES_PER_KWH) {
|
|
PulseCount=0;
|
|
//Serial.println(F("Store kWh counter to EEPROM."));
|
|
stored_kwh_count++;
|
|
EEPROM.put(1,stored_kwh_count);
|
|
}
|
|
|
|
// Convert pulses to Wh for sending to the MQTT broker
|
|
EnergyReading=1000*stored_kwh_count + PulseCount*PULSES_PER_WH;
|
|
}
|
|
|