Recientemente, y debido al proceso de documentación que estoy realizando para el curso de TIA Portal sobre la serie 1200 de Siemens (en el cual veremos ejemplos también en SCL), me he topado con este ejemplo de Seguimiento solar con Siemens 1200 realizado en TIA Portal.
Es un ejemplo publicado en inglés de la empresa Bytex Automation, en Italia. Me he puesto en contacto con ellos para pedirles la oportunidad de traducir el ejemplo a castellano y compartirlo contigo.
Su contacto Massimo me ha dado permiso, por lo que aquí te lo traigo para que lo conozcas si no lo habías visto o bien si la lengua de Shakespeare se te resiste un poco.
La traducción es lo más literal posible para ser fiel a la entrada original.
Seguimiento solar con Siemens 1200 y TIA Portal
Este artículo explica como construir un sistema de seguimiento solar con usando un PLC Simatic S7-1200. La función astronómica estças desarrollada en lenguaje SCL. El objetivo principal es desarrollar una función capaz de retornar la elevación del Sol (ángulo de altitud solar), y su ángulo con el norte (ángulo azimutal).
La función retornará también el tiempo que falta para el amanecer y el ocaso, la duración total de horas de sol, el momento en el que se hace de día y cuándo se hace de noche. Puedes usar esta característica para encender una lámpara en tu jardín cuando el sol se pone o dirigir uno o varios paneles, manejados por ejes motorizados, siguiendo el sol para conseguir el máximo de energía.
![]() |
![]() |
![]() |
Ejemplos de paneles solares usando uno o dos ejes motorizados
La función necesita algunos parámetros de entrada, como la latitud, longitud y elevación local, la zona horaria, ajuste de luz de día, y finalmente la hora actual dada desde el reloj del PLC.
Función astronómica - Parámetros de entrada
- LocalLatitude Número decimal representando la latitud local. Asigna números positivos para el hemisferio norte y negativos para el hemisferio sur. Por ejemplo 45.838073
- LocalLongitude Número decimal que representa la longitud local. Asigna números positivos para meridianos del Este y negativos para el Oeste de Greenwich. Por ejemplo -13.406095)
- Elevation Número decimal que representa la elevación local sobre el mar medido en metros.
- TimeZone (Número decimal que representa la zona horaria respecto a Greenwich.
- DayLightAdjustement Fija DayLightAdjustement = 1 en verano y DayLightAdjustement = 0 para invierno
- ActualDateAndTime Fecha y hora actual
Función astronómica - Parámetros de salida
- AltitudeAngle Número decimal que representa el ángulo entre la posición del sol y el horizonte del observador local.
- AzimuthAngle Número decimal que representa el ángulo entre una dirección de referencia (Norte) y una línea desde el observador hasta la posición del sol proyectada en el mismo plano como la dirección de referencia.
- SunRiseTime Amanecer en tipo de datos DTL
- SunSetTime Puesta del Sol en tipo de datos DTL
- SunDurationTime Duración total de sol en tipo de datos Time
- IsDay Booleana que indica el el evento del amanecer
- IsNight Booleana que indica el evento del anochecer
Cómo funciona: Ajuste de luz solar
Si tu país tiene un sistema de ahorro de luz solar (como por ejemplo España), tienes que ajustar el parámetro DayLightAdjustement=1 en verano, y DayLightAdjustement=0 en invierno. Si no lo tiene, el parámetro tiene que estar siempre a 0.
¿Cómo hacerlo?
Usando la función RD_LOC_T puedes leer la fecha y hora actual del PLC Simatic S7-1200 y leyendo el valor RET_VAL de la función, puedes leer el estado del tiempo de luz solar actual (W#16#0001)
Cómo funciona: Sincronizar el relos del PLC con la hora de internet
¿Cómo hacerlo?
Si tienes una conexión de internet para el PLC es bastante simple. Ve a la configuración del dispositivo en el panel izquierdo del TIA Portal; pica y haz click en el interface PROFINET y selecciona Activar sincronización horaria via servidor NTP y añade una dirección de servidor de hora de internet. Por ejemplo 192.204.114.232.
Cómo funciona: Función astronómica de un sistema de seguimiento solar con Siemens 1200
Lenguaje SCL - Esta es la lista de declaración de variables
// Input parameters
//-------------------------------
// LocalLatitude: Real
// LocalLongitude: Real
// Elevation: Real
// TimeZone: Int
// DayLightAdjustement: Int
// ActualDateAndTime: DTL
// Output parameters
//-------------------------------
// AltitudeAngle: Real
// AzimuthAngle: Real
// SunRiseTime: DTL
// SunSetTime: DTL
// SunDurationTime: Time
// IsDay: Bool
// IsNight: Bool
// PreSet: Bool
// Static declarations
//-------------------------------
// #RadiansToDegrees Real
// #DegreesToRadians Real
// #Meridian Real
// #HoursAfterMidnight Real
// #MinutesAfterMidnight Real
// #UT Real
// #CorrectedYear UInt
// #CorrectedMonth USInt
// #t Real
// #g Real
// #gn Real
// #C Real
// #L Real
// #Alpha Real
// #GHA Real
// #GHAn Real
// #Obliquity Real
// #Declination Real
// #EotAdjustment Real
// #ClockTimeToLSOTAdjustment Real
// #SolarHourAngle Real
// #ApparentSolarTime Real
// #SolarMinutesAfterMidnight Real
// #whichDay Int
// #HourAngle Real
// #PreAzimuthAngle Real
// #x Bool
// #SunRiseSetLSoTMinutes Real
// #SunRiseTime_R Real
// #SunSetTime_R Real
// #SunDurationTime_R Real
// #RHour Real
// #RMin Real
// #RSec Real
// #IHOUR Int
// #IMIN Int
// #ISEC Int
Lenguaje SCL - Código ejemplo de seguimiento solar
//******************************************************
// THIS CODE MUST BE COMPILED AS FUNCTION_BLOCK
//******************************************************
// Define Radians and Degrees conversion
// -------------------------------------
#RadiansToDegrees := 57.295779513082323;
#DegreesToRadians := 0.017453292519943295;
#Meridian := -15.0 * INT_TO_REAL(#TimeZone);
// Calculate Hours after midnight
// ------------------------------
#HoursAfterMidnight := INT_TO_REAL(#ActualDateAndTime.HOUR) +
INT_TO_REAL(#ActualDateAndTime.MINUTE) / 60.0 +
INT_TO_REAL(#ActualDateAndTime.SECOND) / 3600.0;
// Calculate Minutes after midnight
// --------------------------------
#MinutesAfterMidnight := INT_TO_REAL(#ActualDateAndTime.HOUR) * 60.0 +
INT_TO_REAL(#ActualDateAndTime.MINUTE) +
INT_TO_REAL(#ActualDateAndTime.SECOND) / 60.0;
// calculate Universal Time
// ------------------------
#UT := #HoursAfterMidnight - INT_TO_REAL(#TimeZone) - INT_TO_REAL(#DayLightAdjustment);
IF #ActualDateAndTime.MONTH > 2 THEN
#CorrectedYear := #ActualDateAndTime.YEAR;
#CorrectedMonth := #ActualDateAndTime.MONTH - 3;
ELSE
#CorrectedYear := #ActualDateAndTime.YEAR -1;
#CorrectedMonth := #ActualDateAndTime.MONTH + 9;
END_IF;
#t := ((#UT / 24.0) + USINT_TO_REAL(#ActualDateAndTime.DAY) +
DINT_TO_REAL(FLOOR(30.6 * USINT_TO_REAL(#CorrectedMonth) + 0.5)) +
DINT_TO_REAL(FLOOR(365.25 * (UINT_TO_REAL(#CorrectedYear) - 1976.0))) - 8707.5) / 36525.0;
#g := 357.528 + 35999.05 * #t;
#gn := "NormalizeTo360"(#g);
#C := (1.915 * SIN(#gn * #DegreesToRadians)) +
(0.02 * SIN((2.0 * #gn) * #DegreesToRadians));
#L := 280.46 + (36000.77 * #t) + #C;
#Alpha := #L - 2.466 * SIN((2.0 * #L) * #DegreesToRadians) +
0.053 * SIN((4.0 * #L) * #DegreesToRadians);
#GHAn := #UT * 15.0 - 180.0 - #C + #L - #Alpha;
#GHA := "NormalizeTo360"(#GHAn);
#Obliquity := 23.4393 - 0.013 * #t;
#Declination := ATAN(TAN(#Obliquity * #DegreesToRadians) * SIN(#Alpha * #DegreesToRadians));
#Declination := #Declination * #RadiansToDegrees;
#EotAdjustment := (#L - #C - #Alpha) / 15.0; // EOT adjustment in hours
#ClockTimeToLSOTAdjustment := ((#LocalLongitude - #Meridian) / 15.0) -
#EotAdjustment + #DayLightAdjustment;
#SolarHourAngle := 0.0;
#SolarHourAngle := #GHA - #LocalLongitude;
#TempReal := "NormalizeTo180"(#SolarHourAngle);
#SolarHourAngle := #TempReal;
#ApparentSolarTime := 0.0;
#TempReal := 12.0 + #SolarHourAngle / 15.0;
#ApparentSolarTime := "NormalizeTo24"(#TempReal);
#SolarMinutesAfterMidnight := #MinutesAfterMidnight - (#ClockTimeToLSOTAdjustment * 60.0);
#whichDay := 0;
#HourAngle := (#SolarMinutesAfterMidnight - 720.0) / 4.0 * -1.0;
#AltitudeAngle := #RadiansToDegrees *
(ASIN((SIN(#LocalLatitude * #DegreesToRadians) *
SIN(#Declination * #DegreesToRadians)) -
(COS(#LocalLatitude * #DegreesToRadians) *
COS(#Declination * #DegreesToRadians) *
COS((#SolarHourAngle + 180.0) * #DegreesToRadians))));
#PreAzimuthAngle := #RadiansToDegrees * ACOS((COS(#Declination * #DegreesToRadians) *
((COS(#LocalLatitude * #DegreesToRadians) *
TAN(#Declination * #DegreesToRadians)) +
(SIN(#LocalLatitude * #DegreesToRadians) *
COS((#SolarHourAngle + 180.0) * #DegreesToRadians)))) /
COS(#AltitudeAngle * #DegreesToRadians));
#x := False;
IF (#HourAngle > 0.0) THEN
IF (#HourAngle < 180.0) THEN
#x := True;
END_IF;
END_IF;
IF #x THEN
#AzimuthAngle := (360.0 - #PreAzimuthAngle);
ELSE
#AzimuthAngle := #PreAzimuthAngle;
END_IF;
#SunRiseSetLSoTMinutes := #RadiansToDegrees * ACOS(-1.0 *
(SIN(#LocalLatitude * #DegreesToRadians) *
SIN(#Declination * #DegreesToRadians) -
SIN((-0.8333 - 0.0347 * SQRT((#Elevation))) * #DegreesToRadians)) /
COS(#LocalLatitude * #DegreesToRadians) /
COS(#Declination * #DegreesToRadians)) * 4.0;
#SunRiseTime_R := ((720.0 - #SunRiseSetLSoTMinutes + (#ClockTimeToLSOTAdjustment * 60.0)));
#TempReal := #SunRiseTime_R;
#RSec := DINT_TO_REAL(FLOOR((#TempReal - DINT_TO_REAL(FLOOR(#TempReal))) * 60.0));
#RSec := #RSec + DINT_TO_REAL(FLOOR(#TempReal)) * 60.0;
#RHour := DINT_TO_REAL(FLOOR(#RSec / 3600.0));
#RMin := DINT_TO_REAL(FLOOR((#RSec / 3600.0 - #RHour) * 60.0));
#RSec := #RSec - #RHour * 3600.0 - #RMin * 60.0;
IF #RSec >= 30.0 AND #RMin < 59 THEN
#RMin := #RMin + 1.0;
END_IF;
#IHOUR := REAL_TO_INT(#RHour);
#IMIN := REAL_TO_INT(#RMin);
#ISEC := REAL_TO_INT(#RSec);
#SunRiseTime := #ActualDateAndTime;
#SunRiseTime.HOUR := INT_TO_USINT(#IHOUR);
#SunRiseTime.MINUTE := INT_TO_USINT(#IMIN);
#SunRiseTime.SECOND := INT_TO_USINT(#ISEC);
#SunSetTime_R := ((720.0 + #SunRiseSetLSoTMinutes + (#ClockTimeToLSOTAdjustment * 60.0)));
#TempReal := #SunSetTime_R;
#RSec := DINT_TO_REAL(FLOOR((#TempReal - DINT_TO_REAL(FLOOR(#TempReal))) * 60.0));
#RSec := #RSec + DINT_TO_REAL(FLOOR(#TempReal)) * 60.0;
#RHour := DINT_TO_REAL(FLOOR(#RSec / 3600.0));
#RMin := DINT_TO_REAL(FLOOR((#RSec / 3600.0 - #RHour) * 60.0));
#RSec := #RSec - #RHour * 3600.0 - #RMin * 60.0;
IF #RSec >= 30.0 AND #RMin < 59 THEN
#RMin := #RMin + 1.0;
END_IF;
#IHOUR := REAL_TO_INT(#RHour);
#IMIN := REAL_TO_INT(#RMin);
#ISEC := REAL_TO_INT(#RSec);
#SunSetTime := #ActualDateAndTime;
#SunSetTime.HOUR := INT_TO_USINT(#IHOUR);
#SunSetTime.MINUTE := INT_TO_USINT(#IMIN);
#SunSetTime.SECOND := INT_TO_USINT(#ISEC);
#SunDurationTime_R := (#SunSetTime_R - #SunRiseTime_R) / 60.0;
IF #ActualDateAndTime >= #SunRiseTime AND #ActualDateAndTime <= #SunSetTime THEN
#IsDay := true;
#IsNight := false;
ELSE
#IsDay := false;
#IsNight := true;
END_IF;
IF #ActualDateAndTime >= (#SunSetTime - 1) THEN
#PreSet := true;
ELSE
#PreSet := false;
END_IF;
Lenguaje SCL - Código de la función NormalizeTo24
//***********************************************
// Assign to the FUNCTION the name=NormalizeTo24
//***********************************************
// Input parameters
//-------------------------
// aTime: Real
// Return variable
//-------------------------
// Ret_Val Real
#NormalizeTo24 := (#aTime - DINT_TO_REAL(FLOOR(#aTime / 24.0)) * 24.0);
Lenguaje SCL - Código de la función NormalizeTo180
//************************************************
// Assign to the FUNCTION the name=NormalizeTo180
//************************************************
// Input parameters
//-------------------------
// Angle: Real
// Temporary variables
//-------------------------
// TempAngle: Real
// Return variable
//-------------------------
// Ret_Val Real
#TempAngle := "NormalizeTo360"(#Angle);
IF "NormalizeTo360"(#Angle) > 180.0 THEN
#TempAngle := "NormalizeTo360"(#Angle) - 360.0;
END_IF;
#NormalizeTo180 := #TempAngle;
Lenguaje SCL - Código de la función NormalizeTo360
//************************************************
// Assign to the FUNCTION the name=NormalizeTo360
//************************************************
// Input parameters
//-------------------------
// Angle: Real
// Return variable
//-------------------------
// Ret_Val Real
#NormalizeTo360 := (#Angle - DINT_TO_REAL(FLOOR(#Angle / 360.0)) * 360.0);
Últimas recomendaciones sobre esta función para el Seguimiento solar con Siemens 1200 y TIA Portal.
Recuerda llamar al bloque principal dentro de un OB de interrupción fijando el tiempo de interrupción a 1000 milisegundos o 10000 milisegundos.
Esta entrada de Seguimiento solar con Siemens 1200 es original de:
Autor:Bytex Automation es una compañía italiana con muchos años de experiencia internacional en automatización industrial (especialmente en el sector de acero), webs en tiempo real para análisis y manejo de procesos industriales como manufactura y logistica. Su fortaleza radica en la capacidad de desarrollar sistemas que comunican PLC y bases de datos EPR para producir plataformas web integradas y amigables.



Comentarios (16)