/*
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);


    }
}