diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..697b909
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,13 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+ Added : for new features.
+ Changed : for changes in existing functionality.
+ Deprecated: for soon-to-be removed features.
+ Removed : for now removed features.
+ Fixed : for any bug fixes.
+ Security : in case of vulnerabilities.
+
+## [1.0.0] - 2022-11-29
+First working version.
diff --git a/MQTT_energymeter.ino b/MQTT_energymeter.ino
new file mode 100644
index 0000000..06c6b2e
--- /dev/null
+++ b/MQTT_energymeter.ino
@@ -0,0 +1,284 @@
+/**********************************************************************************/
+/* */
+/* 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 . */
+/* */
+/**********************************************************************************/
+
+ #include
+// The connection_data struct needs to be defined in an external file.
+#include
+#include
+//#include "utility/logging.h"
+#include "PubSubClient.h"
+#include //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;
+}
diff --git a/README.md b/README.md
index 77627cf..e71d0ea 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,29 @@
-# atmega328p_mqtt_energy_meter
+# Datalogger reading kWh pulses from a utility grid meter and publising it via MQTT
+(C) 2022 M. Konstapel https://meezenest.nl/mees
+
+Inspired by https://www.instructables.com/A-Simple-MQTT-PubSub-Node-With-Arduino-UNO-and-ENC/
+
+## Features
+
+- Runs on low spec microcontroller (Uno, Nano, Mini, and other ATMega328-based boards)
+- Wired ethernet via ENC28J60 Ethernet module
+- Gets ip address va DHCP
+- Stores data in EEPROM to prevent data loss
+- Easy to configure by telnet port 1000
+
+## Configure
+
+The Device gets its IP address via DHCP. Make sure you have a working DHCP server on the network.
+
+The ip address and port of the MQTT broker can be set by connecting to the device via telnet on port 1000.
+
+To set the ip address of the MQTT broker use the command ": ". For example, to set the ip address to 192.168.1.10 type ": 192.168.1.10" and hit enter.
+
+To set the port number of the MQTT broker use the command "P ". For example, to set the port number to 1883 type "P 1883" and hit enter.
+
+To show the current settings type "I".
+
+To save the settings to EEPROM type "S" and hit enter.
+
+IMPORTANT: after making changes, power cycle the device.