Conditional Compilation
CRBasic allows you to include or exclude sections of code in a program based on the data logger type or other conditions within the program, such as a user-defined constant (see Conditional Compilation Based on Evaluation of Constants). This capability—called conditional compilation—is implemented using #If, #Else, #ElseIf, #EndIf, and #UnDef syntax.The #If_no_remove declaration can be used in place of #If to specify conditional code that should not be removed from the resulting program when a Conditional Compile and Save is performed.
Conditional Compilation Based on data logger Type (LoggerType)
Many CRBasic data loggers use a default file extension that indicates the logger type the program was compiled for (for example, *.CR1X for a CR1000X-series logger).
In addition to data logger-specific file extensions, there are also 2 universal file extensions that allow a program to run on more than one type of data logger:
-
.DLD
-
Original universal file extension
-
Compatible with both retired and current data logger models
-
.CRB
-
New default file extension
-
Not compatible with retired data loggers
-
Will be used for all future data logger models
CR350 data loggers, as well as all GRANITE data loggers use the *.CRB extension. This extension is also valid for CR6 data loggers with OS 11 or later and CR1000X data loggers with OS 5 or later. See Program File Extensions for more information.
By using conditional compilation with a *.DLD or *.CRB file, you can create a single program that runs on multiple data logger types.
A common use case is when the same program must run on data loggers that support different options for a CRBasic instruction. For example, since voltage ranges vary among data logger types, you can use conditional #If/#ElseIf statements to assign the correct range codes. The example program below shows this approach:
Public PTemp, TCTemp
'Set the constant "Range" based on
LoggerType
#If LoggerType = CR6
Const Range=mv200C
#ElseIf LoggerType=CR1000
Const Range=mv2_5C
#EndIf
DataTable (TempTab,True,-1)
DataInterval (0,1,Min,10)
Sample (1,TCTemp,FP2)
EndTable
BeginProg
Scan (1,Sec,3,0)
'Range will bet set to mv200C for a CR6 or mv2_5c for a CR1000
TCDiff (TCTemp,1,Range,U1,TypeT,PTemp,True ,0,15000,1.0,0)
CallTable (TempTab)
NextScan
EndProg
NOTE: Beginning with CR1000X OS 8.4.0, a constant named is_CR1000Xe, can be used within #IF statements to conditionally compile code that is specific to the CR1000Xe.
For example:
#If LoggerType = CR1000X Then
Const Modem_Power = SW12_1
#ElseIf is_CR1000Xe Then
'SW12_CSIO - CR1000Xe Only
Const Modem_Power = SW12_CSIO
#EndIf
Conditional Compilation Based on Evaluation of Constants
The following example program shows how conditional compilation can be used to include or exclude code that is compiled in the data logger based on the state of constants in the program. This program also uses the Custom Menu capability.
Using a keyboard display, the user selects which sensors are connected to the data logger. If a sensor is marked as “true” all the code associated with that sensor (measurements and data table information) is included in the program when compiled. See an example program below.
This program demonstrates how to use conditional compilation based on the evaluation of constants to include or exclude specific sensors and measurement variables.
Public Batt_Volt, Ptemp_C
'Constants for True/False
Const No = False
Const Yes = True
'Constant table for choosing Sensor types
'Set Values to “no” at the beginning of the program
ConstTable
Const Add107 = No
Const Addtc = No
Const Addcs700 = No
Const Addte525 = No
EndConstTable
'MenuRecompile var
Public Recomp
'Create the Custom Menu
DisplayMenu ("CR6 Menu",-3)
DisplayValue ("Compile Msg",status.compileresults)
SubMenu ("Sensor Setup")
MenuItem ("Add Temp107?",Add107)
MenuPick (Add107, Yes, No)
MenuItem ("Add TCTemp?",Addtc)
MenuPick (Addtc, Yes, No)
MenuItem ("Add CS700?",Addcs700)
MenuPick (Addcs700, Yes, No)
MenuItem ("Add TE525?",Addte525)
MenuPick (Addte525, Yes, No)
EndSubMenu
SubMenu ("Save & Compile")
MenuRecompile ("Compile Now", Recomp)
MenuPick (Yes, No)
EndSubMenu
EndMenu
DataTable (TempPrecip,True,1000)
DataInterval (0,1,Min,10)
Sample (1,Batt_Volt,FP2)
#If Add107 Then
Average (1,Temp107,FP2,False)
#EndIf
#If Addtc Then
Average (1,TempTC,FP2,False)
#EndIf
#If Addte525 Then
Totalize (1,RainIn525,FP2,False)
#EndIf
#If Addcs700 Then
Totalize (1,RaininCS700,FP2,False)
#EndIf
EndTable
BeginProg
'Conditionally added
Measurement vars
#If Add107 Then
Public Temp107
#EndIf
#If Addtc Then
Public TempTC
#EndIf
#If Addte525 Then
Public RainIn525
#EndIf
#If Addcs700 Then
Public RaininCS700
#EndIf
Scan (1,Sec,3,0)
'Wiring panel & battery
Battery (Batt_Volt)
'Conditional code for temp sensors
#If Add107 Then
'107 Temperature Probe measurement T107_C
Therm107(Temp107,1,U1,U5,0,15000,1,0)
#EndIf
#If Addtc Then
'Type T (copper-constantan) Thermocouple measurements Temp_C
TCDiff(TempTC,1,mv200C,
#EndIf
'Conditional code for rain gauges
#If Addcs700 Then
'CS700 Rain Gauge measurement Rain_in
PulseCount(RaininCS700,1,U11,2,0,0.01,0)
#EndIf
#If Addte525 Then
'TE525/TE525WS Rain Gauge measurement Rain_in_2
PulseCount(RainIn525,1,U11,2,0,0.01,0)
#EndIf
CallTable (TempPrecip)
NextScan
EndProg
#UnDef
NOTE: The newer IncludeSection() instruction replaces the need to use #UnDef by allowing multiple named sections to be defined within a single include file, also called a file library.
#UnDef is commonly used with Const and #If/EndIf to assemble libraries or sections of code located in Include files. This approach is especially useful in systems with many components and measurements, where large programs often rely on multiple Include files for specific configuration options. Using #If and #UnDef enables conditional declaration of program sections so that multiple Include files can be consolidated into a single file.
For example, in the sample code below, #If is used to declare a constant named Section twice in the main program. The first declaration pulls in sensor settings from an Include file named cpu:Sensor_PT500_Lib.crb, while the second controls data storage using the same Include file. Without #UnDef, declaring the same constant twice would cause a compile error. Using #If together with #UnDef increases program flexibility and reduces the number of Include files required for system configuration.
This program demonstrates how #UnDef functions.
'From Main program:
Const Section = "Section_PT500_Settings"
Include "cpu:Sensor_PT500_Lib.crb"
#UnDef Section
Const Section = "Section_PT500_Storage"
Include "cpu:Sensor_PT500_Lib.crb"
#UnDef Section
'///////////////////////////////////////////////////////////////////////
'From an Include file named "cpu:Sensor_PT500_Lib.crb"
#If Section = "Section_PT500_Settings"
Const AIR_GRASS_TEMP = True
Const GROUND_TEMP = False
#EndIf
#If Section = "Section_PT500_Storage"
DataTable(pt500_12sec, True, -1 )
TableFile("CRD:pt500_12sec", 8, -1, 0, 1, Day, outstat, lastfilename)
DataInterval(0, 12, Sec, 10)
Sample(1, pt500_air, IEEE4, False)
Sample(1, pt500_grass, IEEE4, False)
Sample(1, pt500_t1, IEEE4, False)
EndTable
#EndIf
#if LoggerEndian
The constant, LoggerEndian is used to determine if a data logger is big
endian Endianness refers to byte order. With the little-endian format, bytes are ordered with the least significant byte (the "little end") first. With the big-endian format, bytes are ordered with the most significant byte ("big end") first. The CR300, GRANITE 9, and GRANITE 10 dataloggers use the little-endian format. The CR800, CR1000, CR3000, CR6, CR1000X, and GRANITE 6 use the big-endian format. Byte order when sending string variables as serial data is identical in big-endian and little-endian CSI dataloggers. Only numeric values sent as multiple bytes require attention to big-endian and little-endian issues. of little endian.
LoggerEndian can be compared against CR_LITTLE_ENDIAN and CR_BIG_ENDIAN to determine the endianness of the datalogger.
Usage is as follows:
#If LoggerEndian = CR_LITTLE_ENDIAN
Const endianness_msg = "LITTLE ENDIAN"
#Else
Const endianness_msg = "BIG ENDIAN"
#EndIf
Or inversely
#If LoggerEndian = CR_BIG_ENDIAN
Const endianness_msg = "BIG ENDIAN"
#Else
Const endianness_msg = "LITTLE ENDIAN"
#EndIf
See an example program below:
Public one As Long = 0
'Test conditional compilation
#If LoggerEndian = CR_LITTLE_ENDIAN
Const endianness_msg = "LITTLE ENDIAN"
#Else
Const endianness_msg = "BIG ENDIAN"
#EndIf
Public endianness As String * 50 = "This datalogger is " & endianness_msg
'Test Long value
Public endian As Long = LoggerEndian
Public big_endian As Long = CR_BIG_ENDIAN
Public little_endian As Long = CR_LITTLE_ENDIAN
'Test float value
Public endian_flt = LoggerEndian
Public big_endian_flt = CR_BIG_ENDIAN
Public little_endian_flt = CR_LITTLE_ENDIAN
Public BE_bytes(4) As UINT1 = { &h00, &h00, &h00, &h01 } 'big endian signed integer value of 1
Public LE_bytes(4) As UINT1 = { &h01, &h00, &h00, &h00 } 'little endian signed integer value of 1
BeginProg
'Test run time check (MoveBytes has the transfer parameter for endianness,
'but LoggerEndian allows for more fine-grained control)
If LoggerEndian = CR_BIG_ENDIAN Then
MoveBytes (one,0,BE_bytes(),0,4)
Else
MoveBytes (one,0,LE_bytes(),0,4)
EndIf
EndProg