/* Circuit Setup 6 Channel Energy Monitor based on ATM90E32 For STM32F1, WizNet 5500 Ethernet Version 1: -First version, basic functionality to send power readings via MQTT and display on screen, 2 current channels and voltage To do: -Fix zero wattage reading (Change B to C) -Format screen instead of just dumping -Boot up info to screen Libraries: -ATM90E32: https://github.com/CircuitSetup/ATM90E32 -ArduinoJSON: https://arduinojson.org -ILI9341 TFT:https://github.com/adafruit/Adafruit_ILI9341 */ //SPI library for SPI bus (ATM90E32, Ethernet, etc.) #include "SPI.h" //ATM90E32 library for 6 channel energy monitor board #include <ATM90E32.h> // for ATM90E32 energy meter //ArduinoJSON JSON library #include <ArduinoJson.h> //Ethernet library for W5500 #include <Ethernet.h> //PubSubClinet library for MQTT #include <PubSubClient.h> //Adafruit libraries for ILI934 TFT screen #include "Adafruit_GFX.h" #include "Adafruit_ILI9341.h" #include <Fonts/FreeSans9pt7b.h> //ATM90E32 Calibration Variables /***** CALIBRATION SETTINGS *****/ /* * 4485 for 60 Hz (North America) * 389 for 50 hz (rest of the world) */ unsigned short lineFreq = 4485; /* * 0 for 10A (1x) * 21 for 100A (2x) * 42 for between 100A - 200A (4x) */ unsigned short PGAGain = 21; /* * For meter <= v1.3: * 42080 - 9v AC Transformer - Jameco 112336 * 32428 - 12v AC Transformer - Jameco 167151 * For meter > v1.4: * 37106 - 9v AC Transformer - Jameco 157041 * 38302 - 9v AC Transformer - Jameco 112336 * 29462 - 12v AC Transformer - Jameco 167151 * For Meters > v1.4 purchased after 11/1/2019 and rev.3 * 7611 - 9v AC Transformer - Jameco 157041 */ //unsigned short VoltageGain = 37495; //my 9VAC power adapter, "OEM" unsigned short VoltageGain = 7559; //my 9VAC power adapter, "OEM" /* * 25498 - SCT-013-000 100A/50mA * 39473 - SCT-016 120A/40mA * 46539 - Magnalab 100A */ unsigned short CurrentGainCT1 = 14250; //my 100A CTs SCT013 100A:50mA unsigned short CurrentGainCT2 = 14250; const int CS_pin = PB0; //CS pin for ATM90E32 //CS2 is PB1 //ATM90E32 Data Variables float VoltageA = 0; float VoltageC = 0; float TotalVoltage = 0; float CT1Current = 0; float CT2Current = 0; float TotalCurrent = 0; float TotalWattage = 0; float PowerFactor = 0; float ATM90E32Temp = 0; float LineFrequency = 0; float CT1Wattage = 0; float CT2Wattage = 0; unsigned short sys0 = 0; unsigned short sys1 = 0; unsigned short en0 = 0; unsigned short en1 = 0; //TFT Screen //TFT screen control pins #define TFT_DC PA1 #define TFT_CS PA3 #define TFT_RST PA2 //variables and constants for send timer unsigned long EnergyMonTelePreviousMillis = 0; //the last time the send interval check ran const int EnergyMonitorTelemetryInterval = 2000; //the interval to send, milliseconds //JSON related variables StaticJsonDocument<512> EnergyMeterJSON; //MQTT variables char const* MQTTServer = "192.168.107.11"; //MQTT server address char const* MQTTClientName = "energymon1"; //MQTT client name. Keep short, don't waste RAM const char* EnergyMonitorMQTTTopic = "/energymon/energy/"; //MQTT topic to publish energy info on const char* MQTTTelemetryTopic = "/energymon/tele/"; //MQTT topic to publish telemetry info const char OnlinePayload[] = "ONLINE"; //MQTT payload to send when connected to broker char MQTTEnergyJSON[256]; //cstring to hold JSON data to send via MQTT...might have to be bigger //************************************************* // Make sure to assign a unique MAC to each board! //************************************************* byte mac[] = { 0x21, 0xCA, 0x33, 0x4A, 0xED, 0x52 }; //initialize all the libraries //initialize the ATM90E32 ATM90E32 eic{}; //initialize the IC class //Init TFT library with hardware SPI and control pins above Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST); //Initialize ethernet client as ethClient EthernetClient ethClient; //MQTT callback function, likely not needed, but here just in case void MQTTCallback(char* topic, byte* payload, unsigned int length) { } //Init PubSubClient passing ethClient PubSubClient MQTTClient(MQTTServer, 1883, MQTTCallback, ethClient); //initialize PubSubClient as "MQTTClient" void ConnectToMQTTBroker(bool ShowOnTFT) { while (!MQTTClient.connected()) { // Loop until connected to MQTT broker Serial.println(F("MQTT: Attempting MQTT connection...")); if (ShowOnTFT) { //tft.println(F("MQTT: Attempting MQTT connection...")); } //if connected, publish ONLINE to /energymon/tele/ if (MQTTClient.connect((char*)MQTTClientName)) { Serial.println(F("MQTT: Connected.")); Serial.print(F("MQTT: ")); Serial.print(MQTTTelemetryTopic); Serial.print(F(" = ")); Serial.println(F("ONLINE")); if (ShowOnTFT) { //tft.println(F("MQTT: Connected.")); //tft.print(F("MQTT: ")); //tft.print(MQTTTelemetryTopic); //tft.print(F(" = ")); //tft.println(F("ONLINE")); } MQTTClient.publish_P(MQTTTelemetryTopic, OnlinePayload, false); } //otherwise print failed for debugging else { Serial.print(F("MQTT: Connection failed. Retry in 5 seconds. Error: ")); Serial.println(MQTTClient.state()); if (ShowOnTFT) { //tft.print(F("MQTT: Connection failed. Retry in 5 seconds. Error: ")); //tft.println(MQTTClient.state()); } delay(5000); } } } void setup() { //open the serial port for debugging //Serial.begin(9600); Serial.begin(115200); delay(100); Serial.println(F("BOOT...")); Serial.println(F("TFT: Begin...")); tft.begin(); //set up TFT screen to write boot info tft.fillScreen(ILI9341_BLACK); //blank the screen by filling black tft.setRotation(2); tft.setFont(&FreeSans9pt7b); /*Initialise the ATM90E32 & Pass CS pin and calibrations to its library - CODE IS CURRENTLY FOR SPLIT PHASE...NEED TO MODIFY FOR 6 CHANNEL */ Serial.println(F("ATM90E32: Begin...")); eic.begin(CS_pin, lineFreq, PGAGain, VoltageGain, CurrentGainCT1, CurrentGainCT2, 0); //3 current gains, last one set to 0 as not yet using 3rd channel Serial.println(F("ATM90E32: Success...")); delay(1000); Ethernet.init(PA4); //initialize Ethernet with pin PA4 as CS // start the Ethernet and obtain an address via DHCP Serial.println(F("ETHERNET: Begin...")); if (Ethernet.begin(mac) == 0) { Serial.println(F("ETHERNET: FAIL. No DHCP.")); if (Ethernet.hardwareStatus() == EthernetNoHardware) { Serial.println(F("ETHERNET: Hardware not found.")); } else if (Ethernet.linkStatus() == LinkOFF) { Serial.println(F("ETHERNET: No link.")); } Serial.println(F("STOP")); while (true) { delay(1); } } Serial.print(F("ETHERNET: Success. IP Addresss: ")); Serial.println(Ethernet.localIP()); delay(1500); ConnectToMQTTBroker(false); //Connect to the MQTT broker } void loop() { ConnectToMQTTBroker(false); //if not connected to MQTT broker, connect unsigned long CurrentMillis = millis(); //get the current time count if (CurrentMillis - EnergyMonTelePreviousMillis >= EnergyMonitorTelemetryInterval) { EnergyMonTelePreviousMillis = CurrentMillis; //update the time the check last ran sys0 = eic.GetSysStatus0(); //EMMState0 sys1 = eic.GetSysStatus1(); //EMMState1 en0 = eic.GetMeterStatus0(); //EMMIntState0 en1 = eic.GetMeterStatus1(); //EMMIntState1 Serial.print(F("STATUS: System: S0:0x: ")); Serial.println(sys0, HEX); Serial.print(F("STATUS: System: S1:0x: ")); Serial.println(sys1, HEX); Serial.print(F("STATUS: Meter: E0:0x: ")); Serial.println(en0, HEX); Serial.print(F("STATUS: Meter: E1:0x: ")); Serial.println(en1, HEX); //if true the MCU is not getting data from the energy meter if (sys0 == 65535 || sys0 == 0) { Serial.println(F("ERROR: Not receiving data from energy meter - Check connection.")); } //get voltage VoltageA = eic.GetLineVoltageA(); VoltageC = eic.GetLineVoltageC(); if (lineFreq = 4485) { TotalVoltage = VoltageA + VoltageC; //is split single phase, so only 120v per leg } else { TotalVoltage = VoltageA; //voltage should be 220-240 at the AC transformer } //get current from both transformers and add for a total current measurement CT1Current = eic.GetLineCurrentA(); CT2Current = eic.GetLineCurrentB(); TotalCurrent = CT1Current + CT2Current; //get power. There seems to be no documumentation however guesses: TotalWattage = eic.GetTotalActivePower(); //total wattage PowerFactor = eic.GetTotalPowerFactor(); CT1Wattage = eic.GetActivePowerA(); //CT1 wattage CT2Wattage = eic.GetActivePowerB(); //CT2 wattage ATM90E32Temp = eic.GetTemperature(); LineFrequency = eic.GetFrequency(); Serial.print(F("ENERGY: Voltage A: ")); Serial.println(VoltageA); Serial.print(F("ENERGY: Voltage C: ")); Serial.println(VoltageC); Serial.print(F("ENERGY: Total Voltage: ")); Serial.println(TotalVoltage); Serial.print(F("ENERGY: Current CT1: ")); Serial.println(CT1Current); Serial.print(F("ENERGY: Current CT2: ")); Serial.println(CT2Current); Serial.print(F("ENERGY: Total Current: ")); Serial.println(TotalCurrent); Serial.print(F("ENERGY: CT1 Wattage: ")); Serial.println(CT1Wattage); Serial.print(F("ENERGY: CT2 Wattage: ")); Serial.println(CT2Wattage); Serial.print(F("ENERGY: Total Wattage: ")); Serial.println(TotalWattage); Serial.print(F("ENERGY: Power Factor: ")); Serial.println(PowerFactor); Serial.print(F("ENERGY: Temperature: ")); Serial.println(ATM90E32Temp); Serial.print(F("ENERGY: Frequency: ")); Serial.println(LineFrequency); //tft.fillRect(0, 0, 240, 160,ILI9341_BLACK); tft.fillScreen(ILI9341_BLACK); tft.setCursor(0, 20); //cursor at top left tft.setTextColor(ILI9341_WHITE); //text size white and small tft.setTextSize(1.75); tft.print(F("Voltage A: ")); tft.println(VoltageA); tft.print(F("Voltage C: ")); tft.println(VoltageC); tft.print(F("Total Voltage: ")); tft.println(TotalVoltage); tft.print(F("Current CT1: ")); tft.println(CT1Current); tft.print(F("Current CT2: ")); tft.println(CT2Current); tft.print(F("Total Current: ")); tft.println(TotalCurrent); tft.print(F("CT1 Wattage: ")); tft.println(CT1Wattage); tft.print(F("CT2 Wattage: ")); tft.println(CT2Wattage); tft.print(F("Total Wattage: ")); tft.println(TotalWattage); tft.print(F("Power Factor: ")); tft.println(PowerFactor); tft.print(F("Temperature: ")); tft.println(ATM90E32Temp); tft.print(F("Line Frequency: ")); tft.println(LineFrequency); //add values for JSON collection (ArduinoJSON) EnergyMeterJSON["Sys0"] = sys0; EnergyMeterJSON["Sys1"] = sys1; EnergyMeterJSON["En0"] = en0; EnergyMeterJSON["En1"] = en1; EnergyMeterJSON["VltA"]=VoltageA; EnergyMeterJSON["VltC"] = VoltageC; EnergyMeterJSON["TtlV"] = TotalVoltage; EnergyMeterJSON["CT1"] = CT1Current; EnergyMeterJSON["CT2"] = CT2Current; EnergyMeterJSON["TtlCur"] = TotalCurrent; EnergyMeterJSON["CT1W"] = CT1Wattage; EnergyMeterJSON["CT2W"] = CT2Wattage; EnergyMeterJSON["TtlW"] = TotalWattage; EnergyMeterJSON["PwrFctr"] = PowerFactor; EnergyMeterJSON["ChpTmp"] = ATM90E32Temp; EnergyMeterJSON["LinFrq"] = LineFrequency; //output JSON to serial serializeJson(EnergyMeterJSON, Serial); //Publish via MQTT serializeJson(EnergyMeterJSON, MQTTEnergyJSON); MQTTClient.publish(EnergyMonitorMQTTTopic, MQTTEnergyJSON); } }