First version.

master
marcel 1 year ago
parent 3069e82635
commit c7e60a34d5
  1. 13
      CHANGELOG.md
  2. 284
      MQTT_energymeter.ino
  3. 29
      README.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.

@ -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 <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;
}

@ -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 ": <ip address>". 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 <port number>". 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.

Loading…
Cancel
Save