Site icon Renzo Mischianti

Libreria Inverter ABB Aurora PV per Arduino, esp8266 and esp32

ABB PowerOne Aurora libreria per protocollo di comunicazione per arduino esp8266 esp32 Main

ABB PowerOne Aurora libreria per protocollo di comunicazione per arduino esp8266 esp32 Main

Spread the love

Protocollo ABB Aurora

Ecco intanto le informazioni di base del protocollo di comunicazione RS485 ABB Aurora.

ABB PowerOne Aurora communication protocol Library arduino esp8266 esp32 Main

La comunicazione tra Host e processore funziona tramite un’interfaccia seriale RS485 o RS232.
I parametri di configurazione in entrambi i casi sono:

Il protocollo di comunicazione utilizza messaggi di trasmissione a lunghezza fissa (8Byte + 2Byte per Checksum) strutturati come segue:

0123456789
AddressCommandB2B3B4B5B6B7CRC_LCRC_H

Anche la struttura della risposta ha una lunghezza fissa (6 Byte + 2 Byte per Checksum):

01234567
Transmission StateGlobal StateB2B3B4B5CRC_LCRC_H

Lo stato di trasmissione è codificato come segue:

0 = Va tutto bene.
51 = Il comando non è implementato
52 = La variabile non esiste
53 = Il valore della variabile è fuori intervallo
54 = EEprom non accessibile
55 = Modalità servizio non commutata
56 = Impossibile inviare il comando al micro interno
57 = Comando non eseguito
58 = La variabile non è disponibile, riprova

Global State mostra lo stato del dispositivo indirizzato, i dettagli sono specificati nella descrizione dei comandi.

Ho realizzato questa libreria per realizzare a sua volta questo Monitor Web dell’inverter Aurora

Arduino UNO e MAX485

Puoi usare un Arduino UNO e un IC MAX485, se preferisci puoi acquistare un modulo.

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

Qui il semplice schema di connessione, il resistore deve essere 120Ω, ma io uso 104Ω.

MAX485 Arduino connection schema

Ho creato una libreria derivata da un progetto che puoi trovare nel web creato da drhack, è un lavoro fantastico (grazie a drhack) ma lo trovo abbastanza difficile da usare, con hardware specifico e non così riutilizzabile.

Quindi cerco di standardizzare la libreria e renderla semplice (le persone che usano le mie libreria sanno che “semplificare” è il mio motto.

esp8266 e MAX3485

Se vuoi usare un esp8266 (sto creando una centralina con Wemos D1 mini) devi acquistare un MAX3485 che funzioni alla giusta tensione.

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

Qui il semplice schema di connessione, il resistore deve essere 120Ω, ma io uso un 104Ω.

MAX3485 and esp8266 connection schema

Come puoi vedere è abbastanza semplice connettersi.

Libreria

Puoi trovare la mia libreria qui.

Scaricare.

L’elenco dei comandi è abbastanza ampio, ma proverò a spieghere tutto.

Costruttore

Come al solito cercherò di crearlo il più generico possibile, quindi potrai usarlo con HardwareSerial o SoftwareSerial, sei libero di scegliere.

Come puoi vedere nella descrizione del pacchetto

0123456789
AddressCommandB2B3B4B5B6B7CRC_LCRC_H

è necessario specificare un indirizzo, che normalmente è 2, ma è necessario selezionarlo nel menu dell’inverter.

È possibile creare una catena di inverter che comunicano tramite RS485. È possibile scegliere un indirizzo da 2 a 63. L’impostazione dell’indirizzo sull’inverter avviene tramite il display e la pulsantiera.

Per cambiare l’indirizzo vai a SETTINGS --> Insert password (default 0000) --> Address.

Questo menù permette di impostare gli indirizzi delle porte seriali dei singoli inverter collegati alla linea RS485.
Gli indirizzi assegnabili sono da 2 a 63. I pulsanti UP e DOWN scorrono la scala numerica. 

Al momento non è possibile utilizzare la selezione “AUTO”.

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

Per il software seriale è possibile passare l’istanza SoftwareSerial esterna.

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

Utilizzo

Per prima cosa devi avviare la comunicazione con il comando begin:

inverter.begin();

Ci sono molti comandi che puoi usare per fare query al tuo inverter:

Richiesta stato inverter

Il primo comando importante è readState per ottenere lo stato del tuo inverter:

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

La struttura in risposta ha 4 attributi e 4 funzioni:

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

Naturalmente gli attributi sono il codice, la funzione è la descrizione dello stato.

Versione inverter

Con questo comando si ottiene la versione del software dell’inverter:

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

La struttura in risposta ha 4 attributi e 6 funzioni:

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

InverterModel ha questa struttura:

    int ID;
    float multipler;
    String name;

par1 identifica il modello, e così posso creare 2 funzioni di descrizione una con il modello specifico, la seconda il tipo di modello;

par2 identifico lo standard di rete e creo 2 funzioni per ottenere lo standard di rete;

par3 specifica se è trasformer less;

par4 specifica se è versione Wind o versione PV

Misura la richiesta al DSP

Con questo comando puoi ottenere la tensione, la corrente ecc. Ecc. C’è una grande lista di parametri che converto in una lista di costanti che puoi usare.

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

Il comando è semplice:

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

global
se 1 richiede le misurazioni globali (solo per un master)
se 0 richiede le Misure Modulo (Master e Slave)

e anche la struttura del risultato è semplice:

            float value;
            OutcomeState state;

Dove valore è il valore richiesto, ha queste unità di misura:

VoltagesV
CurrentsA
PowersW
Temperatures°C

OutcomeState è lo stato di ritorno che ha questa struttura:

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

Dove lo stato di trasmissione descrive se il comando è scritto correttamente, lo stato globale è lo stato globale dell’inverter, readState è lo stato della chiamata: se vero l’inverter risponde, falso no.

Lettura ora / data

Ottieni il tempo impostato sull’Inverter in secondi ed epochTime, e aggiungo 2 funzioni una che restituisce la stringa dell’ora e un’altra che restituisce la struttura standard di Arduino.

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

Ultimi quattro allarmi

Questo comando restituisce i codici degli ultimi quattro allarmi, sotto forma di coda FIFO dal primo
(AL1) all’ultimo (AL4).
Quando si utilizza questo comando la coda viene svuotata (i quattro valori sono posti a zero).

Aurora::DataLastFourAlarms dataLastFourAlarms = inverter.readLastFourAlarms()

struttura di ritorno

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

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

Scatola di giunzione – Richiesta stato

Verificare il parametro e lo stato della scatola di giunzione.

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

NJ: numero scatola di giunzione

Qui il risultato della struttura

        	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;

Aggiungo un JBoxState per semplificare la lettura, qui la struttura

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

Lettura P / N (inverter Aurora)

Legge la PN dell’inverter

Aurora::DataSystemPN dataSystemPN = inverter.readSystemPN()

return a simple structure

            String PN;
            bool readState;

Lettura del numero di serie (inverter Aurora)

Aurora::DataSystemSerialNumber dataSystemSerialNumber = inverter.readSystemSerialNumber()

e qui la struttura di ritorno

            String SerialNumber;
            bool readState;

Lettura della settimana e dell’anno di produzione (inverter Aurora)

Aurora::DataManufacturingWeekYear dataManufacturingWeekYear = inverter.readManufacturingWeekYear()

e qui la struttura di ritorno

            String Week;
            String Year;
            OutcomeState state;

Lettura della versione del firmware

Aurora::DataFirmwareRelease dataFirmwareRelease = inverter.readFirmwareRelease()

e qui la struttura di ritorno

            String release;
            OutcomeState state;

Letture di energia accumulata (solo inverter Aurora collegati alla rete)

Leggi l’energia accumulata del parametro che passi

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

qui il parametro

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

qui la struttura

            unsigned long energy;
            OutcomeState state;

Configurazione di sistema

Aurora::DataConfigStatus dataConfigStatus = inverter.readConfig()

qui la struttura

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

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

qui il parametro

#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)"

qui la struttura di ritorno

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

Codice di esempio

Ecco un esempio di lettura tramite 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((byte)1);
    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);

}

Come puoi vedere l’utilizzo è abbastanza semplice.

Video dimostrativo

Qui il video della chiamata dei risultati.

Grazie

GitHub repository


Spread the love
Exit mobile version