Site icon Renzo Mischianti

ABB Aurora PV inverter library for Arduino, esp8266 and esp32

Spread the love

ABB Aurora protocol

Here the base information of RS485 ABB Aurora communication Protocol.

ABB PowerOne Aurora communication protocol Library arduino esp8266 esp32 Main

The communication between Host and processor works via a Serial Interface RS485 or RS232.
Configuration parameters in both cases are:

The communication protocol uses fixed length transmission messages (8Bytes + 2Bytes for Checksum) structured as follows:

0123456789
AddressCommandB2B3B4B5B6B7CRC_LCRC_H

Also the structure of the answer has fixed length (6 Bytes + 2 Bytes for Checksum) :

01234567
Transmission StateGlobal StateB2B3B4B5CRC_LCRC_H

Transmission State is coded as follows:

0 = Everything is OK.
51 = Command is not implemented
52 = Variable does not exist
53 = Variable value is out of range
54 = EEprom not accessible
55 = Not Toggled Service Mode
56 = Can not send the command to internal micro
57 = Command not Executed
58 = The variable is not available, retry

Global State shows the state of the addressed device, the details are specified in the description of the commands.

I create this library to develop this Web Monitor interface.

Arduino UNO and MAX485

You can use an Arduino UNO and a MAX485 IC, if you prefer can buy a module.

You can find IC on AliExpress

You can find module on AliExpress

You can ArduinoUNO on Arduino UNO - Arduino MEGA 2560 R3 - Arduino Nano - Arduino Pro Mini

Here the simple connection schema, the resistor must be 120Ω, i use 104Ω.

MAX485 Arduino connection schema

I create a library derived from a project that you can find in the web created by drhack, It’s a fantastic works (thanks to drhack) but I find It quite difficult to use, with specific hardware and not so reusable.

So I try to standardize the library and made It simple (the people that use my library know that “simplify first” It’s my motto.

esp8266 and MAX3485

If you want use an esp8266 (I create a centraline with Wemos D1 mini) you must buy a MAX3485 tha work at the correct voltage.

You can find IC on AliExpress

You can find module on AliExpressAliExpress

You can find WeMos D1 mini on WeMos D1 mini - NodeMCU V2 V2.1 V3 - esp01 - esp01 programmer

Here the simple connection schema, the resistor must be 120Ω, i use 104Ω.

MAX3485 and esp8266 connection schema

As you can see It’s quite simple to connect.

Library

You can find my library here.

To download.

List of commands It’s quite big, so I’m going to explain all.

Constructor

As usual I try to create It more generic as possible, so If you want use HardwareSerial or SoftwareSerial you are free to choiche.

As you can see in the packet description

0123456789
AddressCommandB2B3B4B5B6B7CRC_LCRC_H

you must specify an address, that address normally is 2, but you must check It in your inverter menu.

You can create a chain of inverters that communicate via RS485. An address can be chosen from 2 to 63. The address on the inverter is set through the display and the pushbutton panel.

To change address go to SETTINGS --> Insert password (default 0000) --> Address.

This menu allows you to set the serial port addresses of the individual inverters connected to the RS485 line.
The addresses that can be assigned are 2 to 63. The UP and DOWN buttons scroll through the numerical scale. ‘AUTO’ selection cannot be used at present.

HardwareSerial

// Aurora(byte inverterAddress, HardwareSerial* serial, byte serialCommunicationControlPin)
Aurora inverter = Aurora(2, &Serial, 5);

SoftwareSerial

// Aurora(byte inverterAddress, byte rxPin, byte txPin, byte serialCommunicationControlPin)
Aurora inverter = Aurora(2, 10, 11, 5);

For software serial you can pass external SoftwareSerial instance.

// Aurora(byte inverterAddress, SoftwareSerial* serial, byte serialCommunicationControlPin) 

How to

First you must startup the communication with begin command:

inverter.begin();

Than there are a lot of command that you can use to make query to your Inverter:

Inverter state request

First important command is readState to get the state of your Inverter:

	Aurora::DataState dataState = inverter.readState();

The structure result have 4 attribute and 4 function:

            byte inverterState;
            byte channel1State;
            byte channel2State;
            byte alarmState;
            String getDcDcChannel1State()
            String getDcDcChannel2State()
            String getInverterState()
            String getAlarmState()

Naturally the attribute are the code, the function is the description of the state.

Inverter version

This command get the version of the inverter software:

Aurora::DataVersion dataVersion = inverter.readVersion();

The structure result have 4 attribute and 6 function:

            char par1;
            char par2;
            char par3;
            char par4;
            InverterModel getModelName()
            String getIndoorOutdoorAndType()
            String getGridStandard()
            String getGridStandardInt()
            String getTrafoOrNonTrafo()
            String getWindOrPV()

InverterModel have this structure:

    int ID;
    float multipler;
    String name;

par1 identify the model, and so I can create 2 description function one with the specific model, the second the type of the model;

par2 identify the grid standard and I create 2 function to get the grid standard that change in some part;

par3 specify if It’s trasformer less;

par4 specify if It’s Wind or PV version

Measure request to the DSP

With this command you can get the voltage, current etc.. etc.. there is a big list of parameter that I convert in a list of constant that you can use.

#define DSP_GRID_VOLTAGE_ALL								1   /* Grid Voltage (All) */
#define DSP_GRID_CURRENT_ALL								2   /* Grid Currrent (All) */
#define DSP_GRID_POWER_ALL									3   /* Grid Power (All) */
#define DSP_FREQUENCY_ALL									4   /* Frequency All) */
#define DSP_VBULK_WAS_ILEAK_DCDC_READING_ALL				5   /* VBulk was Ileak (Dc/Dc) reading All) */
#define DSP_ILEAK_DCDC_WAS_ILEAK_INVERTER_READING			6   /* Ileak (Dc/Dc) was Ileak (Inverter) Reading*/
#define DSP_ILEAK_INVERTER									7   /* Ileak (Inverter) */
#define DSP_PIN1_ALL										8   /* Pin 1 (All) */
#define DSP_PIN2											9   /* Pin 2 */
#define DSP_INVERTER_TEMPERATURE_GT							21  /* Inverter Temperature (Grid-Tied) */
#define DSP_BOOSTER_TEMPERATURE_GT							22  /* Booster Temperatuer (Grid-Tied) */
#define DSP_VOLTAGE_ALL										23  /* Input 1 Voltage */
#define DSP_CURRENT_ALL 									25  /* Input 1 Current (All) */
#define DSP_VOLTAGE_GT										26  /* Input 2 Voltage (Grid-Tied) */
#define DSP_CURRENT_GT										27  /* Input 2 Current (Grid-Tied) */
#define DSP_GRID_VOLTAGE_DCDC_GT							28  /* Grid Voltage (Dc/Dc) (Grid-Tied) */
#define DSP_GRID_FREQUENCY_DCDC_GT							29  /* Grid Frequency (Dc/Dc) (Grid-Tied) */
#define DSP_ISOLATION_RESISTANCE_RISO_ALL					30  /* Isolation Resistance (Riso) (All) */
#define DSP_VBULK_DCDC_GT									31  /* Vbulk (Dc/Dc) (Grid-Tied) */
#define DSP_AVERAGE_GRID_VOLTAGE_VGRIDAVG_GT				32  /* Average Grid Voltage (VgridAvg) (Grid-Tied) */
#define DSP_VBULKMID_GT										33  /* Vbulk Mid (Grid-Tied) */
#define DSP_POWER_PEAK_ALL									34  /* Power Peak (All) */
#define DSP_POWER_PEAK_TODAY_ALL							35  /* Power Peak Today (All) */
#define DSP_GRID_VOLTAGE_NEUTRAL_GT							36  /* Grid Voltage neutral (Grid-Tied) */
#define DSP_WIND_GENERATOR_FREQUENCY						37  /* Wind Generator Frequency */
#define DSP_GRID_VOLTAGE_NEUTRAL_PHASE_CENTRAL				38  /* Grid Voltage neutral-phase (Central) */
#define DSP_GRID_CURRENT_PHASE_R_CENTRAL_3P					39  /* Grid Current phase r (Central & 3 Phase) */
#define DSP_GRID_CURRENT_PHASE_S_CENTRAL_3P					40  /* Grid Current phase s (Central & 3 Phase) */
#define DSP_GRID_CURRENT_PHASE_T_CENTRAL_3P					41  /* Grid Current phase t (Central & 3 Phase) */
#define DSP_FREQUENCY_PHASE_R_CENTRAL_3P					42  /* Frequency phase r (Central & 3 Phase) */
#define DSP_FREQUENCY_PHASE_S_CENTRAL_3P					43  /* Frequency phase s (Central & 3 Phase) */
#define DSP_FREQUENCY_PHASE_T_CENTRAL_3P					44  /* Frequency phase t (Central & 3 Phase) */
#define DSP_VBULK_PLUS_CENTRAL_3P							45  /* Vbulk + (Central & 3 Phase) */
#define DSP_VBULK_MINUS_CENTRAL								46  /* Vbulk - (Central) */
#define DSP_SUPERVISOR_TEMPERATURE_CENTRAL					47  /* Supervisor Temperature (Central) */
#define DSP_ALIM_TEMPERATURE_CENTRAL						48  /* Alim Temperature (Central) */
#define DSP_HEAT_SINK_TEMPERATURE_CENTRAL					49  /* Heak Sink Temperature (Central) */
#define DSP_TEMPERATURE_1_CENTRAL							50  /* Temperature 1 (Central) */
#define DSP_TEMPERATURE_2_CENTRAL							51  /* Temperature 2 (Central) */
#define DSP_TEMPERATURE_3_CENTRAL							52  /* Temperature 3 (Central) */
#define DSP_FAN_1_SPEED_CENTRAL								53  /* Fan 1 Speed (Central) */
#define DSP_FAN_2_SPEED_CENTRAL								54  /* Fan 2 Speed (Central) */
#define DSP_FAN_3_SPEED_CENTRAL								55  /* Fan 3 Speed (Central) */
#define DSP_FAN_4_SPEED_CENTRAL								56  /* Fan 4 Speed (Central) */
#define DSP_FAN_5_SPEED_CENTRAL								57  /* Fan 5 Speed (Central) */
#define DSP_POWER_SATURATION_LIMIT_DER_CENTRAL				58  /* Power Saturation limit (Der.) (Central) */
#define DSP_RIFERIMENTO_ANELLO_BULK_CENTRAL					59  /* Reference Ring Bulk (Central) */
#define DSP_VPANEL_MICRO_CENTRAL							60  /* Vpanel micro (Central) */
#define DSP_GRID_VOLTAGE_PHASE_R_CENTRAL_3P					61  /* Grid Voltage phase r (Central & 3 Phase) */
#define DSP_GRID_VOLTAGE_PHASE_S_CENTRAL_3P					62  /* Grid Voltage phase s (Central & 3 Phase) */
#define DSP_GRID_VOLTAGE_PHASE_T_CENTRAL_3P					63  /* Grid Voltage phase t (Central & 3 Phase) */
#define DSP_FAN1_SPEED_CENTRAL								95  /* Fan 1 Speed (rpm) (Central) */
#define DSP_FAN2_SPEED_RPM_CENTRAL								96  /* Fan 2 Speed (rpm) (Central) */
#define DSP_FAN3_SPEED_RPM_CENTRAL								97  /* Fan 3 Speed (rpm) (Central) */
#define DSP_FAN4_SPEED_RPM_CENTRAL								98  /* Fan 4 Speed (rpm) (Central) */
#define DSP_FAN5_SPEED_RPM_CENTRAL								99  /* Fan 5 Speed (rpm) (Central) */
#define DSP_FAN6_SPEED_RPM_CENTRAL								100 /* Fan 6 Speed (rpm) (Central) */
#define DSP_FAN7_SPEED_RPM_CENTRAL								101 /* Fan 7 Speed (rpm) (Central) */

The command is simple:

Aurora::DataDSP dataDSP = inverter.readDSP(byte type, byte global)

global
if 1 requires the Global Measurements (Only For a Master)
if 0 requires the Module Measurements (Master and Slave)

and the result structure is simple too:

            float value;
            OutcomeState state;

Where value is the value requested, with this measurement units:

VoltagesV
CurrentsA
PowersW
Temperatures°C

the OutcomeState is the returning state that have this structure:

			byte transmissionState;
			byte globalState;
			bool readState;
			String getTransmissionState()
			String getGlobalState()

Where transmission state describe if the command is correctly spelled, the global state is the global state of the inverter, the readState is the state of the calling: true inverter respond false no.

Time/Date reading

Get the time set on Inverter in seconds and epochTime, and I add 2 function one that return the String of the time and another that return tm standard Arduino structure.

Aurora::DataTimeDate dataTimeDate = inverter.readTimeDate();
            unsigned long seconds;
            unsigned long epochTime;
            String getDateTimeString()
            tm getDateTime()

Last four alarms

This command returns the codes of the last four alarms, in form of a FIFO queue from the first
(AL1) to the last one (AL4).
When this command is used the queue is emptied (the four values are set to zero).

Aurora::DataLastFourAlarms dataLastFourAlarms = inverter.readLastFourAlarms()

returning structure

        	byte alarm1;
        	byte alarm2;
        	byte alarm3;
        	byte alarm4;

                String getAlarm1State()
                String getAlarm2State()
                String getAlarm3State()
                String getAlarm4State()

Junction Box – State Request

Check the parameter and state of junction box.

Aurora::DataJunctionBoxState dataJunctionBoxState = inverter.readJunctionBoxState(byte nj)

NJ: Junction Box Number

Here the structure result

        	JBoxState jBoxState;
        	bool fuseBurnt[21] = {false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false};
        	bool stringCurrentUnbalanced[11] = {false,false,false,false,false,false,false,false,false,false,false} ;
            OutcomeState state;

I add a JBoxState to simplify the reading, here the structure

        	bool burnFuseOnJBox;
        	bool jBoxOvertemperature;
        	bool jBoxOvervoltage;
        	bool unbalancedStringCurrent;
        	bool jBoxOvercurrent;
        	bool powerOff;
        	bool noCommunication;
        	bool jBoxNotCalibrated;

P/N Reading (Aurora inverters)

Read the inverter PN

Aurora::DataSystemPN dataSystemPN = inverter.readSystemPN()

return a simple structure

            String PN;
            bool readState;

Serial Number reading (Aurora inverters)

Aurora::DataSystemSerialNumber dataSystemSerialNumber = inverter.readSystemSerialNumber()

and here the returning structure

            String SerialNumber;
            bool readState;

Manufacturing Week and Year reading (Aurora inverters)

Aurora::DataManufacturingWeekYear dataManufacturingWeekYear = inverter.readManufacturingWeekYear()

and here the returning structure

            String Week;
            String Year;
            OutcomeState state;

Firmware release reading

Aurora::DataFirmwareRelease dataFirmwareRelease = inverter.readFirmwareRelease()

and here the returning structure

            String release;
            OutcomeState state;

Cumulated energy readings (Aurora grid-tied inverters only)

Read the cumulated energy of the parameter that you pass

Aurora::DataCumulatedEnergy dataCumulatedEnergy = inverter.readCumulatedEnergy(byte par)

here the parameter

#define CUMULATED_DAILY_ENERGY				0				/* Daily Energy */
#define CUMULATED_WEEKLY_ENERGY  			1				/* Weekly Energy */
#define CUMULATED_ENERGY_OF_LAST_7_DAYS  		2				/* Energy of last 7 days */
#define CUMULATED_MONTHLY_ENERGY  			3				/* Monthly Energy */
#define CUMULATED_YEARLY_ENERGY  			4				/* Yearly Energy */
#define CUMULATED_TOTAL_ENERGY_LIFETIME  		5				/* Total Energy (total lifetime) */
#define CUMULATED_PARTIAL_ENERGY_SINCE_RESET  	        6				/* Partial Energy (cumulated since reset) */

here the simple structure

            unsigned long energy;
            OutcomeState state;

System Configuration

Aurora::DataConfigStatus dataConfigStatus = inverter.readConfig()

here the simple structure

        	byte configStatus;
        	String getConfigStatus() {
				return getConfigFlagByParams(this->configStatus);
			}
        	OutcomeState state;

Aurora::DataTimeCounter dataTimeCounter = inverter.readTimeCounter(byte param)

here the parameter

#define	CT_TOTAL_RUN		0
#define CT_PARTIAL_RUN		1
#define CT_TOTAL_GRID		2
#define CT_RESET_PARTIAL	3

#define CT_TOTAL_RUN_DESC	"Total Running Time (Lifetime)"
#define CT_PARTIAL_RUN_DESC	"Partial Running Time (since reset)"
#define CT_TOTAL_GRID_DESC	"Total Time With Grid Connection"
#define CT_RESET_PARTIAL_DESC	"Reset of Partial (Time & Energy)"

here the returning structure

        	unsigned long upTimeInSec;
        	int * getSecondsInDateElements() {
        		return getSecondsInDateElementsByParams(this->upTimeInSec);
			}
        	OutcomeState state;

Example code

Here an example of reading via Arduino.

/*
 Test Arduino MAX485 Aurora ABB connection
 by Mischianti Renzo <https://mischianti.org>

 https://www.mischianti.org/
*/

#include "Arduino.h"
#include <Aurora.h>
#include <SoftwareSerial.h>
#include <MemoryFree.h>

//SoftwareSerial mySerial(10, 11); // RX, TX
//Aurora inverter = Aurora(2, &Serial1, 5);
Aurora inverter = Aurora(2, 10, 11, 5);

void SerialPrintData(byte *data) {
  for (int i = 0; i < 8; i++) {
    Serial.print((int)data[i]);
    Serial.print(F(" "));
  }
  Serial.println(F(" "));

}

void setup()
{
	Serial.begin(19200);
	inverter.begin();
}

// The loop function is called in an endless loop
void loop()
{
	Serial.print(F("freeMemory(1)="));Serial.println(freeMemory());

	Aurora::DataCumulatedEnergy cumulatedEnergy = inverter.readCumulatedEnergy(CUMULATED_DAILY_ENERGY);
    Serial.println(F("------------------------------------------"));
    Serial.println(F("INVERTER 2"));
    Serial.print(F("          Data ROW = ")); SerialPrintData(inverter.receiveData);
    Serial.print(F("        Read State = ")); Serial.println(cumulatedEnergy.state.readState);
    Serial.print(F("Transmission State = ")); Serial.println(cumulatedEnergy.state.getTransmissionState());
    Serial.print(F("      Global State = ")); Serial.println(cumulatedEnergy.state.getGlobalState());
    Serial.print(F("           Energia = ")); Serial.print(cumulatedEnergy.energy); Serial.println(" Wh");
//    free(&cumulatedEnergy);
    Serial.println(F("------------------------------------------"));


	Aurora::DataLastFourAlarms lastFour = inverter.readLastFourAlarms();

    Serial.println(F("INVERTER 2"));
    Serial.print(F("          Data ROW = "));  SerialPrintData(inverter.receiveData);
	Serial.print(F("        Read State = "));  Serial.println(lastFour.state.readState);
    Serial.print(F("Transmission State = "));  Serial.println(lastFour.state.getTransmissionState());
    Serial.print(F("      Global State = "));  Serial.println(lastFour.state.getGlobalState());
    Serial.print(F("          Alarms 1 = "));  Serial.println(lastFour.getAlarm1State());
    Serial.print(F("          Alarms 2 = "));  Serial.println(lastFour.getAlarm2State());
    Serial.print(F("          Alarms 3 = "));  Serial.println(lastFour.getAlarm3State());
    Serial.print(F("          Alarms 4 = "));  Serial.println(lastFour.getAlarm4State());
//	free(&lastFour);

    Serial.println(F("------------------------------------------"));

    Aurora::DataVersion version = inverter.readVersion();
    Serial.println("INVERTER 2");
    Serial.print(F("          Data ROW = ")); SerialPrintData(inverter.receiveData);
    Serial.print(F("        Read State = ")); Serial.println(version.state.readState);
    Serial.print(F("Transmission State = ")); Serial.println(version.state.getTransmissionState());
    Serial.print(F("      Global State = ")); Serial.println(version.state.getGlobalState());
    Serial.print(F("           Version = ")); Serial.print(version.getModelName().name); Serial.print(F(" ")); Serial.print(version.getIndoorOutdoorAndType()); Serial.print(F(" ")); Serial.print(version.getGridStandard()); Serial.print(F(" ")); Serial.print(version.getTrafoOrNonTrafo()); Serial.print(F(" ")); Serial.println(version.getWindOrPV());
    Serial.println(F("------------------------------------------"));
//	free(&version);

    Aurora::DataConfigStatus configStatus = inverter.readConfig();

    Serial.print(F("          Data ROW = ")); SerialPrintData(inverter.receiveData);
    Serial.print(F("        Read State = ")); Serial.println(configStatus.state.readState);
    Serial.print(F("Transmission State = ")); Serial.println(configStatus.state.getTransmissionState());
    Serial.print(F("      Global State = ")); Serial.println(configStatus.state.getGlobalState());
    Serial.print(F("      config       = ")); Serial.println(configStatus.getConfigStatus());
    Serial.println(F("------------------------------------------"));
//	free(&version);
    Serial.print(F("freeMemory(2)="));Serial.println(freeMemory());

    Aurora::DataTimeCounter timeCounter = inverter.readTimeCounter(CT_TOTAL_RUN);

    Serial.print(F("          Data ROW = ")); SerialPrintData(inverter.receiveData);
    Serial.print(F("        Read State = ")); Serial.println(timeCounter.state.readState);
    Serial.print(F("Transmission State = ")); Serial.println(timeCounter.state.getTransmissionState());
    Serial.print(F("      Global State = ")); Serial.println(timeCounter.state.getGlobalState());
    Serial.print(F("      time in sec  = ")); Serial.println(timeCounter.upTimeInSec);
    Serial.print(F("      time in verb  = ")); Serial.print(timeCounter.getSecondsInDateElements()[0]); Serial.print(F("Y ")); Serial.print(timeCounter.getSecondsInDateElements()[1]); Serial.print(F("D "));Serial.print(timeCounter.getSecondsInDateElements()[2]);Serial.print(F("H "));Serial.print(timeCounter.getSecondsInDateElements()[3]);+Serial.print(F("M "));Serial.print(timeCounter.getSecondsInDateElements()[4]);Serial.println(F("S "));
    Serial.println(F("------------------------------------------"));
//	free(&version);
    Serial.print(F("freeMemory(2)="));Serial.println(freeMemory());

    delay(4000);

}

As you can see the usage is quite simple.

Video

Here the video of the result call.

Thanks

GitHub repository


Spread the love
Exit mobile version