_stInverterData); // =============================== // Read modbus request count // =============================== _timADSReadTimer(IN := NOT _fbADSReader.BUSY, PT := T#200MS); _fbADSReader( NETID:= '', PORT:= 10500, IDXGRP:= 16#2000, IDXOFFS:= 1, LEN:= 4, DESTADDR:= ADR(GVL_MODBUS.stModbusEMSComm.stModbusReg11.udiLifeMessage), READ:= _timADSReadTimer.Q, TMOUT:= T#1S, BUSY=> , ERR=> , ERRID=> ); // =============================== // MQTT cummunication // =============================== // Handle connection to broker _fbMQTTClient.Execute(_xConnectToMQTTBrocker); // =============================== // State machine // =============================== CASE _iState OF 0: // Idle // Wait for power command IF (ABS(GVL_MODBUS.stModbusEMSComm.stModbusReg12.diSetpointActivePower) > GVL_CONFIG.diMinimumAbsPowerForEnable) AND _fbString.xSafetyIntlksOk AND (NOT _fbString.xError) THEN _iState := 5; END_IF 5: // Check if power command is within limits IF GVL_MODBUS.stModbusEMSComm.stModbusReg12.diSetpointActivePower < GVL_CONFIG.diMaxStringDischargePower AND GVL_MODBUS.stModbusEMSComm.stModbusReg12.diSetpointActivePower > GVL_CONFIG.diMaxStringChargingPower THEN _xEnableString := TRUE; _iState := 10; ELSE // Set error bitmap flag GVL_MODBUS.stModbusEMSComm.stModbusReg11.lwErrorBitmap.0 := 1; // Goto error state _iState := 1000; END_IF 10: // Wait for string to be ready IF _fbString.xReady AND (NOT _fbString.xError) THEN _iState := 20; END_IF 20: // Start main inverter with zero power _rPowerInverter := 0.0; _xEnableInverter := TRUE; _iState := 25; 25: // Wait for inverter to be ready IF _fbInverter.xActive AND (NOT _fbInverter.xError) THEN // Set battery status for modbus GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus := E_BATTERY_STATUS.ACTIVE; GVL_MODBUS.stModbusEMSComm.stModbusReg10.uiActiveParallelMembers := 1; _iState := 30; END_IF // Check for errors IF _fbString.xError OR _fbInverter.xError THEN _iState := 1000; END_IF 30: // String and inverter enabled // Set inverter power to modbus requested power _rPowerInverter := DINT_TO_REAL(GVL_MODBUS.stModbusEMSComm.stModbusReg12.diSetpointActivePower); // Check if the battery should still be active IF (GVL_MODBUS.stModbusEMSComm.stModbusReg12.diSetpointActivePower = 0) THEN _xNoPowerRequested := TRUE; ELSE _xNoPowerRequested := FALSE; END_IF // Set battery status IF GVL_MODBUS.stModbusEMSComm.stModbusReg12.diSetpointActivePower > 0 THEN GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus := E_CHARGE_STATUS.DISCHARGING; ELSIF GVL_MODBUS.stModbusEMSComm.stModbusReg12.diSetpointActivePower < 0 THEN GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus := E_CHARGE_STATUS.CHARGING; ELSE GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus := E_CHARGE_STATUS.UNDEFINED; END_IF // Add small delay before shutdown by EMS is detected _tonBeginShutdown(IN := _xNoPowerRequested); // shutdown triggered from EMS IF _tonBeginShutdown.Q THEN _tonBeginShutdown(In := FALSE); // Set inverter to zero power _rPowerInverter := 0.0; // Start string shutdown _xEnableString := FALSE; GVL_MODBUS.stModbusEMSComm.stModbusReg10.uiActiveParallelMembers := 0; _iState := 35; END_IF // Shutdown triggered by battery fully charged IF GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus = E_CHARGE_STATUS.CHARGING AND (_fbString.rCurrentVoltage >= GVL_CONFIG.rStringFullyChargedVoltage) THEN _tonBeginShutdown(In := FALSE); // Set inverter to zero power _rPowerInverter := 0.0; // Start string shutdown _xEnableString := FALSE; // Change battery status GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus := E_CHARGE_STATUS.FULL; GVL_MODBUS.stModbusEMSComm.stModbusReg10.uiActiveParallelMembers := 0; _iState := 35; END_IF // Shutdown triggered by battery empty IF GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus = E_CHARGE_STATUS.DISCHARGING AND (_fbString.rCurrentVoltage <= GVL_CONFIG.rStringEmptyVoltage) THEN _tonBeginShutdown(In := FALSE); // Set inverter to zero power _rPowerInverter := 0.0; // Start string shutdown _xEnableString := FALSE; // Change battery status GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus := E_CHARGE_STATUS.FULL; GVL_MODBUS.stModbusEMSComm.stModbusReg10.uiActiveParallelMembers := 0; _iState := 35; END_IF // Check for errors IF _fbString.xError OR _fbInverter.xError THEN _tonBeginShutdown(In := FALSE); _iState := 1000; END_IF 35: // Wait for string to be in shutdown discharge mode IF _fbString.xInShutdownDischargeMode THEN // Check if we are allowed to discharge during shutdown with inverter IF GVL_CONFIG.xShutdownDischargeWithInverter THEN _iState := 40; ELSE _rPowerInverter := 0.0; _xEnableInverter := FALSE; _iState := 45; END_IF END_IF // Check for errors IF _fbString.xError OR _fbInverter.xError THEN _iState := 1000; END_IF 40: // Wait for inverter discharge done IF _fbString.xShutdownDischargeAllowed THEN _rPowerInverter := GVL_CONFIG.rAbsShutdownDischargePower; ELSE _rPowerInverter := 0.0; _xEnableInverter := FALSE; _iState := 45; END_IF // Check for errors IF _fbString.xError OR _fbInverter.xError THEN _iState := 1000; END_IF 45: // Wait for shutdown of string to be done IF (NOT _fbString.xInShutdownDischargeMode) AND _fbString.xOff THEN GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus := E_BATTERY_STATUS.OFF; _iState := 0; END_IF // Check for errors IF _fbString.xError OR _fbInverter.xError THEN _iState := 1000; END_IF 1000: // Error state _xEnableString := FALSE; _xEnableInverter := FALSE; _rPowerInverter := 0.0; GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus := E_BATTERY_STATUS.ERROR; _iState := 1010; 1010: // Wait for reset from error state IF (GVL_MODBUS.stModbusEMSComm.stModbusReg12.diSetpointActivePower = 0) AND (NOT _fbString.xError) AND (NOT _fbInverter.xError) THEN // Reset modbus error register GVL_MODBUS.stModbusEMSComm.stModbusReg11.lwErrorBitmap := 0; // Reset modbus error flag GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus := E_BATTERY_STATUS.OFF; // Goto init state _iState := 0; END_IF END_CASE // Send MQTT battery status _xBatteryStatusChange := FALSE; IF GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus <> _eLastBatteryStatus THEN _xBatteryStatusChange := TRUE; END_IF _eLastBatteryStatus := GVL_MODBUS.stModbusEMSComm.stModbusReg11.eBatteryStatus; IF GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus <> _eLastChargeStatus THEN _xBatteryStatusChange := TRUE; END_IF _eLastChargeStatus := GVL_MODBUS.stModbusEMSComm.stModbusReg11.eChargeStatus; IF _xBatteryStatusChange THEN SendBatteryStatus(); END_IF // Reset alarm confirmation IF _xConfirmAlarms THEN _xConfirmAlarms := FALSE; END_IF]]> _sJSONString); // Send message if connected and no formatting error occured IF NOT _fbFormatString.bError AND _fbMQTTClient.bConnected THEN _fbMQTTClient.Publish('status', ADR(_sJSONString), INT_TO_UDINT(LEN(_sJSONString)), GVL_MQTT.eMQTTQoS, FALSE, FALSE); END_IF]]>