parent
3069e82635
commit
c7e60a34d5
3 changed files with 325 additions and 1 deletions
@ -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…
Reference in new issue