1 // ***********************************************************************************************************
    2 //   Custom DPA Handler code example - Standard Sensors + Binary output - DDC-SE-01 + DDC-RE-01 - LP version *
    3 // ***********************************************************************************************************
    4 // Copyright (c) MICRORISC s.r.o.
    5 //
    6 // File:    $RCSfile: 4402_DDC-SE+RE_LP.c,v $
    7 // Version: $Revision: 1.29 $
    8 // Date:    $Date: 2022/09/12 12:34:37 $
    9 //
   10 // Revision history:
   11 //   2022/02/24  Release for DPA 4.17
   12 //   2021/08/20  Release for DPA 4.16
   13 //   2020/01/02  Release for DPA 4.11
   14 //   2019/03/07  Release for DPA 4.01
   15 //
   16 // *********************************************************************
   17 
   18 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/
   19 // IQRF Standards documentation https://doc.iqrf.org/
   20 
   21 // This example implements 3 sensors according to the IQRF Sensors standard
   22 // Index 0 i.e. 1st sensor is either Dallas 18B20 or MCP9802 temperature sensor at DDC-SE-01 board according to the HW jumper position and symbol DALLASnotMCP.
   23 // Index 1 i.e. 2nd sensor is light intensity indicator at DDC-SE-01 board (value range is 0[max light]-127[max dark]).
   24 // Index 2 i.e. 3rd sensor is potentiometer value at DDC-SE-01 board (value range is 0[left stop]-127[right stop]).
   25 
   26 // This example also implements 2 binary outputs according to the IQRF Binary Outputs standard
   27 // Index 0 i.e. 1st output is Relay #1 @ DDC-RE-01
   28 // Index 1 i.e. 2nd output is Relay #2 @ DDC-RE-01
   29 
   30 // This example must be compiled without a "-bu" compiler switch in order to fit into available Flash memory
   31 
   32 // Default IQRF include (modify the path according to your setup)
   33 #include "IQRF.h"
   34 
   35 // We can save more instructions if needed by the symbol below
   36 // #define  PARAM_CHECK_LEVEL 1
   37 
   38 // Default DPA header (modify the path according to your setup)
   39 #include "DPA.h"
   40 // Default Custom DPA Handler header (modify the path according to your setup)
   41 #include "DPAcustomHandler.h"
   42 // IQRF standards header (modify the path according to your setup)
   43 #include "IQRFstandard.h"
   44 #include "IQRF_HWPID.h"
   45 
   46 // If defined then the handler is compiled for Dallas otherwise for MCP9802
   47 //#define   DALLASnotMCP
   48 // !!! Important: with free C5XX compiler, the symbol DALLASnotMCP must not be defined, otherwise the code does not fit into available program memory @ TR7xD.
   49 
   50 //############################################################################################
   51 
   52 // Define useful macro that saves some code but not preset at DPA < 3.01
   53 #if DPA_VERSION_MASTER  < 0x0301
   54 // Optimized macro for both testing enumeration peripherals ELSE peripherals information. See examples
   55 #define IfDpaEnumPeripherals_Else_PeripheralInfo_Else_PeripheralRequestNoSize() if ( _PCMD == CMD_GET_PER_INFO ) if ( _PNUM == PNUM_ENUMERATION )
   56 
   57 #if PARAM_CHECK_LEVEL >= 2
   58 #define IfDpaEnumPeripherals_Else_PeripheralInfo_Else_PeripheralRequest() if ( _DpaDataLength == 0 && _PCMD == CMD_GET_PER_INFO ) if ( _PNUM == PNUM_ENUMERATION )
   59 #else
   60 #define IfDpaEnumPeripherals_Else_PeripheralInfo_Else_PeripheralRequest() IfDpaEnumPeripherals_Else_PeripheralInfo_Else_PeripheralRequestNoSize()
   61 #endif
   62 #endif
   63 
   64 //############################################################################################
   65 
   66 // Number of implemented sensors
   67 #define SENSORS_COUNT 3
   68 
   69 // Variable to store sensor value at Get?_????() methods. This example implements sensors returning maximum 2 bytes of data.
   70 uns16 sensorValue @ param3;
   71 
   72 // Reads sensor value to the sensorValue variable and to responseFRCvalue(2B) variable
   73 bit Get0_Temperature();
   74 bit Get1_BinaryData_Light();
   75 bit Get2_BinaryData_Potentiometer();
   76 
   77 // Stores sensor value byte(s) to the FSR1[+1...], in case of PCMD_STD_SENSORS_READ_TYPES_AND_VALUES sensor type is stored before value byte(s)
   78 void StoreValue( uns8 sensorType );
   79 
   80 // ms per ticks
   81 #define TICKS_LEN  10
   82 
   83 #ifdef DALLASnotMCP
   84 // Sensor connected to PORT C.3 (compatible with DDC-SE-01)
   85 #define OneWire_TRIS         TRISC.3
   86 #define OneWire_IO_IN        PORTC.3
   87 #define OneWire_IO_OUT       LATC.3
   88 
   89 // Writes sensor configuration (resolution)
   90 bit Ds18B20WriteConfig( uns8 value );
   91 
   92 // Resets OneWire
   93 bit OneWireReset();
   94 // Reads OneWire byte
   95 uns8 OneWireReadByte();
   96 // Writes OneWire byte
   97 void OneWireWriteByte( uns8 byte );
   98 
   99 // DS18B20 commands
  100 #define CMD_READROM       0x33
  101 #define CMD_CONVERTTEMP   0x44
  102 #define CMD_CPYSCRATCHPAD 0x48
  103 #define CMD_WSCRATCHPAD   0x4e
  104 #define CMD_MATCHROM      0x55
  105 #define CMD_RPWRSUPPLY    0xb4
  106 #define CMD_RECEEPROM     0xb8
  107 #define CMD_RSCRATCHPAD   0xbe
  108 #define CMD_SKIPROM       0xcc
  109 #define CMD_ALARMSEARCH   0xec
  110 #define CMD_SEARCHROM     0xf0
  111 
  112 // Final DS18B20 temperature value read by state machine
  113 uns16 temperature;
  114 
  115 #else // DALLASnotMCP
  116 
  117 // I2C SCL frequency [Hz]
  118 #define I2Cfrequency      50000
  119 
  120 // MCP9802 address
  121 #define I2C_ADR           0b1001.0110
  122 
  123 #endif
  124 
  125 //############################################################################################
  126 
  127 // Number of implemented binary outputs
  128 #define OUTPUTS_COUNT 2
  129 
  130 // Sets and Gets state of the indexed binary output
  131 void SetOutput( uns8 state, uns8 index );
  132 bit GetOutput( uns8 index );
  133 
  134 // DDC-RE-01 relay pins
  135 //  C.5 = C8 = Relay#1
  136 #define RELAY1_LAT  LATC.5
  137 #define RELAY1_TRIS TRISC.5
  138 //  C.2 = C2 = Relay#2
  139 #define RELAY2_LAT  LATC.2
  140 #define RELAY2_TRIS TRISC.2
  141 
  142 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
  143 //############################################################################################
  144 bit CustomDpaHandler()
  145 //############################################################################################
  146 {
  147   // This forces CC5X to wisely use MOVLB instructions (doc says:  The 'default' bank is used by the compiler for loops and labels when the algorithm gives up finding the optimal choice)
  148 #pragma updateBank default = UserBank_01
  149 
  150 #ifdef DALLASnotMCP
  151   // Finite machine states
  152   typedef enum
  153   {
  154     S_ResetConvertT = 0,
  155     S_SkipRomConvertT,
  156     S_CmdConvertT,
  157 
  158     S_WaitConvertT,
  159 
  160     S_ResetReadTemp,
  161     S_SkipRomReadTemp,
  162     S_CmdReadTemp,
  163     S_Byte1ReadTemp,
  164     S_Byte2ReadTemp
  165   } TState;
  166 #endif
  167 
  168   // Handler presence mark
  169   clrwdt();
  170 
  171   // Sleeping parameters, valid when Time != 0
  172   static TPerOSSleep_Request PerOSSleep_Request;
  173 
  174 #ifdef DALLASnotMCP
  175   // Finite machine state
  176   static uns8 state; // = S_ResetConvertT = 0
  177   // Pre-read lower temperature byte
  178   static uns8 temperatureByteLow;
  179   // Conversion timeout counter
  180   static uns16 timeoutStart;
  181 #endif
  182 
  183   // Timers for outputs. The space must be long enough to fit them all. 2+2 bytes per one binary output.
  184   //  2B timeout
  185   //  2B startTicks
  186   static uns16  Timers[OUTPUTS_COUNT * 2];
  187 
  188   // Detect DPA event to handle
  189   switch ( GetDpaEvent() )
  190   {
  191     // -------------------------------------------------
  192     case DpaEvent_Interrupt:
  193       // Do an extra quick background interrupt work
  194 
  195       return Carry;
  196 
  197       // -------------------------------------------------
  198     case DpaEvent_Idle:
  199       // Do a quick background work when RF packet is not received
  200 
  201       // Should go to sleep?
  202       if ( PerOSSleep_Request.Time != 0 )
  203       {
  204         // Copy sleep parameters to the DPA request
  205         _DpaMessage.PerOSSleep_Request.Time = PerOSSleep_Request.Time;
  206         _DpaMessage.PerOSSleep_Request.Control = PerOSSleep_Request.Control;
  207         // Finalize OS Sleep DPA Request
  208         _DpaDataLength = sizeof( _DpaMessage.PerOSSleep_Request );
  209         _PNUM = PNUM_OS;
  210         _PCMD = CMD_OS_SLEEP;
  211 #ifndef DALLASnotMCP
  212         // I2C down
  213         _DpaApiI2Cshutdown();
  214 #endif
  215         // Perform local DPA Request to go to sleep
  216         DpaApiLocalRequest();
  217 #ifndef DALLASnotMCP
  218         // I2C up
  219         _DpaApiI2Cinit( I2CcomputeFrequency( I2Cfrequency ) );
  220 #endif
  221         // Switch off sleeping time=flag
  222         PerOSSleep_Request.Time = 0;
  223       }
  224 
  225       // Check binary output timers
  226       {
  227         // Pointer to the timers array
  228         FSR1 = (uns16)&Timers[0];
  229         // Output index
  230         uns8 index;
  231         index = 0;
  232         do
  233         {
  234           // Is timer running (is non-zero)?
  235           if ( ( FSR1[1] | INDF1 ) != 0 )
  236           {
  237             // Get timer value
  238             uns16 timer;
  239             timer.low8 = FSR1[0];
  240             timer.high8 = FSR1[1];
  241             // Get start time
  242             uns16 timerStart;
  243             timerStart.low8 = FSR1[2];
  244             timerStart.high8 = FSR1[3];
  245             // Measure elapsed time
  246             captureTicks(); // Note: must not modify FSR1
  247             param3 -= timerStart;
  248             // It time over?
  249             if ( param3 > timer )
  250             {
  251               // Set output to OFF
  252               SetOutput( 0, index );
  253               // Reset timer
  254               setINDF1( 0 );
  255               FSR1++;
  256               setINDF1( 0 );
  257               FSR1--;
  258             }
  259           }
  260           // Next timer
  261           FSR1 += 2 * sizeof( Timers[0] );
  262           // Next index
  263         } while ( ++index < OUTPUTS_COUNT );
  264       }
  265 
  266 #ifdef DALLASnotMCP
  267       // Run finite state machine to read temperature from DS18B20 at background so the temperature value is immediately ready for FRC
  268 
  269       // Make sure 1Wire data pin at LATX.y is low as it might be set by another PORTX.? pin manipulation
  270       OneWire_IO_OUT = 0;
  271 
  272       skip( state );
  273 #pragma computedGoto 1
  274       goto _S_ResetConvertT;
  275       goto _S_SkipRomConvertT;
  276       goto _S_CmdConvertT;
  277       goto _S_WaitConvertT;
  278       goto _S_ResetReadTemp;
  279       goto _S_SkipRomReadTemp;
  280       goto _S_CmdReadTemp;
  281       goto _S_Byte1ReadTemp;
  282       goto _S_Byte2ReadTemp;
  283 #pragma computedGoto 0
  284       ;
  285       // --------------
  286 _S_Byte2ReadTemp:
  287       temperature.high8 = OneWireReadByte();
  288       temperature.low8 = temperatureByteLow;
  289 
  290 ResetMachine:
  291       state = S_ResetConvertT;
  292       goto ExitMachine;
  293 
  294       // --------------
  295 _S_ResetConvertT:
  296 _S_ResetReadTemp:
  297       if ( !OneWireReset() )
  298       {
  299 _S_Error_Reset:
  300         STD_SENSOR_TYPE_TEMPERATURE_SET_ERROR( temperature );
  301         goto ResetMachine;
  302       }
  303       goto NextState;
  304 
  305       // --------------
  306 _S_SkipRomConvertT:
  307 _S_SkipRomReadTemp:
  308       // OneWire: Skip ROM
  309       OneWireWriteByte( CMD_SKIPROM );
  310       goto NextState;
  311 
  312       // --------------
  313 _S_CmdConvertT:
  314       // OneWire: Convert temperature
  315       OneWireWriteByte( CMD_CONVERTTEMP );
  316       // Start timeout for approx 750 ms (the longest conversion time)
  317       captureTicks();
  318       // Remember start time
  319       timeoutStart = param3;
  320       goto NextState;
  321 
  322       // --------------
  323 _S_WaitConvertT:
  324       // Measured?
  325       if ( OneWireReadByte() == 0xff )
  326         goto NextState;
  327 
  328       // Timeout?
  329       captureTicks();
  330       param3 -= timeoutStart;
  331       // Yes!
  332       if ( param3 > ( 2 + 750 / TICKS_LEN ) )
  333         goto _S_Error_Reset;
  334 
  335       goto ExitMachine;
  336 
  337       // --------------
  338 _S_CmdReadTemp:
  339       // OneWire: Read scratchpad
  340       OneWireWriteByte( CMD_RSCRATCHPAD );
  341       goto NextState;
  342 
  343       // --------------
  344 _S_Byte1ReadTemp:
  345       temperatureByteLow = OneWireReadByte();
  346       goto NextState;
  347 
  348       // --------------
  349 NextState:
  350       ++state;
  351 
  352 ExitMachine:
  353 #endif
  354       break;
  355 
  356       // -------------------------------------------------
  357     case DpaEvent_Init:
  358       // Do a one time initialization before main loop starts
  359 
  360       // Initialize ticks
  361       startCapture();
  362 
  363       // Initialize relays @ DDC-RE
  364       RELAY1_LAT = 0;
  365       RELAY2_LAT = 0;
  366       RELAY1_TRIS = 0;
  367       RELAY2_TRIS = 0;
  368 
  369       // Initialize sensors
  370       // C5 (AN4) as input
  371       moduleInfo();
  372       // Connected TR pins?
  373       if ( !bufferINFO[5].7 )
  374       {
  375         TRISC.6 = 1;
  376         TRISB.4 = 1;
  377       }
  378       TRISA.5 = 1;
  379 
  380       // C1 (AN0) as input
  381       TRISA.0 = 1;
  382 
  383 #ifdef DALLASnotMCP
  384       // Setup DS18B20 for 9bit precision, conversion takes 94ms (see datasheet)
  385       Ds18B20WriteConfig( 0b0.00.00000 );
  386 #else
  387       // Expect MCP9802 is enabled
  388       I2Ctimeout = 0xFF;
  389 
  390 #if defined( TR7xG )
  391       // Do PPS for I2C
  392       unlockPPS();
  393       SSP1CLKPPS = 0x13;  // RC3
  394       SSP1DATPPS = 0x14;  // RC4
  395       RC3PPS = 0x14;      // SCK1/SCL1
  396       RC4PPS = 0x15;      // SD01/SDA1
  397       lockPPS();
  398 #endif
  399 
  400       _DpaApiI2Cinit( I2CcomputeFrequency( I2Cfrequency ) );
  401 #endif
  402       break;
  403 
  404       // -------------------------------------------------
  405     case DpaEvent_AfterSleep:
  406       // Called after woken up after sleep
  407 #ifndef DALLASnotMCP
  408       _DpaApiI2Cinit( I2CcomputeFrequency( I2Cfrequency ) );
  409 #endif
  410 
  411       break;
  412 
  413       // -------------------------------------------------
  414     case DpaEvent_BeforeSleep:
  415       // Called before going to sleep
  416 #ifndef DALLASnotMCP
  417       _DpaApiI2Cshutdown();
  418 #endif
  419       break;
  420 
  421       // -------------------------------------------------
  422     case DpaEvent_DpaRequest:
  423       // Called to interpret DPA request for peripherals
  424       // -------------------------------------------------
  425       // Peripheral enumeration
  426       IfDpaEnumPeripherals_Else_PeripheralInfo_Else_PeripheralRequest()
  427       {
  428         // We implement 2 standard peripherals
  429         _DpaMessage.EnumPeripheralsAnswer.UserPerNr |=  2;
  430         FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_STD_SENSORS );
  431         FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_STD_BINARY_OUTPUTS );
  432         _DpaMessage.EnumPeripheralsAnswer.HWPID |= HWPID_IQRF_TECH__DEMO_DDC_SE01_RE01_LP;
  433         _DpaMessage.EnumPeripheralsAnswer.HWPIDver |= 0x0000;
  434 
  435 DpaHandleReturnTRUE:
  436         return TRUE;
  437       }
  438       // -------------------------------------------------
  439       // Get information about peripherals
  440       else
  441       {
  442 #if PERIPHERAL_TYPE_STD_SENSORS != PNUM_STD_SENSORS || PERIPHERAL_TYPE_STD_BINARY_OUTPUTS != PNUM_STD_BINARY_OUTPUTS
  443 #error
  444 #endif
  445         switch ( _DpaMessage.PeripheralInfoAnswer.PerT = _PNUM )
  446         {
  447           case PNUM_STD_SENSORS:
  448             // Set standard version
  449             W = STD_SENSORS_VERSION;
  450             goto Par1toVersion;
  451 
  452           case PNUM_STD_BINARY_OUTPUTS:
  453             // Set standard version
  454             W = STD_BINARY_OUTPUTS_VERSION;
  455 Par1toVersion:
  456             _DpaMessage.PeripheralInfoAnswer.Par1 = W;
  457             _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ;
  458             goto DpaHandleReturnTRUE;
  459         }
  460 
  461         break;
  462       }
  463 
  464       {
  465         // -------------------------------------------------
  466         // Handle peripheral command
  467 
  468         // Supported peripheral number?
  469         switch ( _PNUM )
  470         {
  471           case PNUM_STD_SENSORS:
  472           {
  473             // Supported commands?
  474             switch ( _PCMD )
  475             {
  476               // Invalid command
  477               default:
  478               {
  479                 // Return error
  480 _ERROR_PCMD:
  481                 W = ERROR_PCMD;
  482 _ERROR_W:
  483                 DpaApiReturnPeripheralError( W );
  484               }
  485 
  486               // Sensor enumeration
  487               case PCMD_STD_ENUMERATE:
  488                 if ( _DpaDataLength != 0 )
  489                   goto _ERROR_DATA_LEN;
  490 
  491                 _DpaMessage.Response.PData[0] = STD_SENSOR_TYPE_TEMPERATURE;
  492                 _DpaMessage.Response.PData[1] = STD_SENSOR_TYPE_BINARYDATA7;
  493                 _DpaMessage.Response.PData[2] = STD_SENSOR_TYPE_BINARYDATA7;
  494                 W = SENSORS_COUNT;
  495                 goto _W2_DpaDataLength;
  496 
  497                 // Supported commands. They are handled the same way except one "if" at StoreValue() method
  498               case PCMD_STD_SENSORS_READ_VALUES:
  499               case PCMD_STD_SENSORS_READ_TYPES_AND_VALUES:
  500               {
  501                 // No sensor bitmap specified? W = _DpaDataLength. Note: W is used to avoid MOVLB at next if
  502                 W = _DpaDataLength;
  503                 if ( W == 0 )   // Note: must not modify W
  504                 {
  505                   // Actually clears the bitmap
  506 #if &_DpaMessageIqrfStd.PerStdSensorRead_Request.Bitmap[0] != &bufferRF[0]
  507 #error Cannot use clearBufferRF for clearing bitmap
  508 #endif
  509                   clearBufferRF();
  510                   // Simulate 1st only sensor in the bitmap (states of the other unimplemented sensors do not care)
  511                   _DpaMessageIqrfStd.PerStdSensorRead_Request.Bitmap[0].0 = 1;
  512                   // Bitmap is 32 bits long
  513                   _DpaDataLength = W = sizeof( _DpaMessageIqrfStd.PerStdSensorRead_Request.Bitmap );
  514                 }
  515 
  516                 // Invalid bitmap (data) length (W = _DpaDataLength)?
  517                 if ( W != sizeof( _DpaMessageIqrfStd.PerStdSensorRead_Request.Bitmap ) )
  518                   goto _ERROR_DATA_LEN;
  519 
  520                 // Now read the sensors
  521 
  522                 // Prepare pointer (minus 1, see below) to store sensor (types and) values to
  523                 // Note: 3 sensors at this example cannot return more than DPA_MAX_DATA_LENGTH bytes of data, so it does not have to be checked...
  524                 // ... If it would be the case, then ERROR_FAIL must be returned
  525                 FSR1 = &_DpaMessage.Response.PData[-1];
  526 
  527                 // Store bitmap of sensors to get values from
  528                 uns8 sensorsBitmap = FSR1[1 + offsetof( TPerStdSensorRead_Request, Bitmap )];
  529 
  530                 // 1st sensor (index 0) selected?
  531                 if ( sensorsBitmap.0 )
  532                 {
  533                   Get0_Temperature();
  534                   StoreValue( STD_SENSOR_TYPE_TEMPERATURE );
  535                 }
  536 
  537                 // 2nd sensor (index 1) selected?
  538                 if ( sensorsBitmap.1 )
  539                 {
  540                   Get1_BinaryData_Light();
  541                   StoreValue( STD_SENSOR_TYPE_BINARYDATA7 );
  542                 }
  543 
  544                 // 3rd sensor (index 2) selected?
  545                 if ( sensorsBitmap.2 )
  546                 {
  547                   Get2_BinaryData_Potentiometer();
  548                   StoreValue( STD_SENSOR_TYPE_BINARYDATA7 );
  549                 }
  550 
  551                 // Compute returned data bytes count
  552                 W = FSR1L - ( (uns16)&_DpaMessageIqrfStd & 0xFF ) + 1;
  553                 // Optimization: return W long block of bytes at response
  554 _W2_DpaDataLength:
  555                 _DpaDataLength = W;
  556                 goto DpaHandleReturnTRUE;
  557               }
  558             }
  559           }
  560 
  561           case PNUM_STD_BINARY_OUTPUTS:
  562           {
  563             // Supported commands?
  564             switch ( _PCMD )
  565             {
  566               // Invalid command
  567               default:
  568                 // Return error
  569                 goto _ERROR_PCMD;
  570 
  571                 // Outputs enumeration
  572               case PCMD_STD_ENUMERATE:
  573                 if ( _DpaDataLength != 0 )
  574                   goto _ERROR_DATA_LEN;
  575 
  576                 // Return number of outputs
  577                 _DpaMessageIqrfStd.PerStdBinaryOutputEnumerate_Response.Count = OUTPUTS_COUNT;
  578                 W = sizeof( _DpaMessageIqrfStd.PerStdBinaryOutputEnumerate_Response );
  579                 goto _W2_DpaDataLength;
  580 
  581                 // Supported commands.
  582               case PCMD_STD_BINARY_OUTPUTS_SET:
  583               {
  584                 // Pointers FSR01 to data are already set at the DPA
  585 
  586                 // As this template implements < 9 outputs the working bitmap is uns8, if more outputs are implemented then uns16, ..., uns32 must be used
  587 #if OUTPUTS_COUNT < 9
  588                 uns8 inBitmap = *FSR0--;
  589                 uns8 outBitmap @ _DpaMessageIqrfStd.PerStdBinaryOutputSet_Request.Bitmap[0];
  590                 uns8 bitmapMask = 0b1;
  591 #else
  592 #error Not implemented
  593 #endif
  594 
  595               // Number of selected outputs + bitmap length
  596                 uns8 outputsCount = sizeof( _DpaMessageIqrfStd.PerStdBinaryOutputSet_Request.Bitmap );
  597                 // Loop bitmap
  598                 uns8 index = sizeof( _DpaMessageIqrfStd.PerStdBinaryOutputSet_Request.Bitmap );
  599                 do
  600                 {
  601                   // Count bits of next byte
  602                   uns8 byte = *++FSR0;
  603                   if ( byte != 0 )
  604                   {
  605                     // Brian Kernighan's Algorithm for counting set bits
  606                     do
  607                     {
  608                       outputsCount++;
  609                       byte &= byte - 1;
  610                     } while ( byte != 0 );
  611                   }
  612 
  613                   // Reset bitmap
  614                   setINDF0( 0 );
  615                 } while ( --index != 0 );
  616 
  617                 // Check data length
  618                 if ( _DpaDataLength != outputsCount )
  619                 {
  620 _ERROR_DATA_LEN:
  621                   W = ERROR_DATA_LEN;
  622                   goto _ERROR_W;
  623                 }
  624 
  625                 // Pointer to the timers array
  626                 FSR1 = (uns16)&Timers[0];
  627                 // Output index
  628                 index = 0;
  629                 do
  630                 {
  631                   // Output was set?
  632                   if ( GetOutput( index ) )
  633                     // Yes, set in the output bitmap
  634                     outBitmap |= bitmapMask;
  635 
  636                   // Implemented output selected? Set the state.
  637                   if ( inBitmap.0 )
  638                   {
  639                     // Default is timer off
  640                     uns16 time = 0;
  641                     // Desired state
  642                     uns8 state = *++FSR0;
  643                     if ( state > 1 )
  644                     {
  645                       // Get time in units s/min
  646                       time = state & 0x7F;
  647                       if ( time == 0 )
  648                       {
  649                         // Invalid time
  650                         W = ERROR_FAIL;
  651 _ERROR_FAIL:
  652                         goto _ERROR_W;
  653                       }
  654 
  655                       // Conversion coefficient, ready for seconds
  656                       uns16 coef = 1000 / TICKS_LEN;
  657                       if ( !state.7 )
  658                       {
  659                         // Check for the maximum supported time because of captureTicks method
  660                         if ( time.low8 > ( (uns24)0xFFFF * TICKS_LEN / 1000 / 60 ) )
  661                           goto _ERROR_FAIL;
  662 
  663                         // Convert from minutes
  664                         uns16 coef = 60 * ( 1000 / TICKS_LEN );
  665                       }
  666 
  667                       // Convert to 250 ms
  668                       time *= coef;
  669                       // Set ON
  670                       state = 1;
  671                     }
  672 
  673                     // Set output
  674                     SetOutput( state, index );
  675 
  676                     // Set timer but preserve pointer
  677                     setINDF1( time.low8 );
  678                     FSR1++;
  679                     setINDF1( time.high8 );
  680                     FSR1++;
  681                     // Get start time
  682                     captureTicks(); //Note: must not destroy FSR1
  683                     setINDF1( param3.low8 );
  684                     FSR1++;
  685                     setINDF1( param3.high8 );
  686                     FSR1 -= 3;
  687                   }
  688 
  689                   // Pointer to the next timer
  690                   FSR1 += 2 * sizeof( Timers[0] );
  691                   // Next bits
  692                   bitmapMask <<= 1;
  693                   inBitmap >>= 1;
  694                   // Next index
  695                 } while ( ++index < OUTPUTS_COUNT );
  696 
  697                 // Return bitmap
  698 _DpaDataLength4:
  699                 W = sizeof( _DpaMessageIqrfStd.PerStdBinaryOutputSet_Response.PreviousStates );
  700                 goto _W2_DpaDataLength;
  701               }
  702             }
  703           }
  704         }
  705 
  706         break;
  707       }
  708 
  709       // -------------------------------------------------
  710     case DpaEvent_FrcValue:
  711       // Called to get FRC value
  712 
  713       // FSR1 for optimization purposes (avoid MOVLB) will be used to point to DataOutBeforeResponseFRC[0...]
  714       FSR1 = (uns16)&PerStdSensorFrc;
  715 #if offsetof( TPerStdSensorFrc, Header ) != 0 || offsetof( TPerStdSensorFrc, SensorType ) != 1 || offsetof( TPerStdSensorFrc, Options ) != 3
  716 #error Cannot optimize
  717 #endif
  718       // Check for correct FRC user data
  719       if ( *FSR1++ /* PerStdSensorFrc.Header */ == PNUM_STD_SENSORS )
  720       {
  721         // Actually used sensor index
  722         uns8 sensorIndex = FSR1[offsetof( TPerStdSensorFrc, SensorIndex ) - 1] & 0x1f;
  723         // Test sensor type
  724         switch ( *FSR1++ /* PerStdSensorFrc.SensorType */ )
  725         {
  726           default:
  727             goto DpaHandleReturnFALSE;
  728 
  729             // No type specified, use specified index value
  730           case 0x00:
  731             goto _KeepSensorIndex;
  732 
  733             // For other types make the index value based on the requested index value and sensor type
  734           case STD_SENSOR_TYPE_TEMPERATURE:
  735             if ( sensorIndex > 0 )
  736               goto DpaHandleReturnFALSE;
  737             W = 0 + sensorIndex;
  738             break;
  739 
  740           case STD_SENSOR_TYPE_BINARYDATA7:
  741             if ( sensorIndex > 1 )
  742               goto DpaHandleReturnFALSE;
  743             W = 1 + sensorIndex;
  744             break;
  745         }
  746 
  747         // New sensor index based on type and requested index
  748         sensorIndex = W;
  749 _KeepSensorIndex:
  750 
  751         // Test for supported FRC commands
  752         switch ( _PCMD )
  753         {
  754           default:
  755             goto DpaHandleReturnFALSE;
  756 
  757           case FRC_STD_SENSORS_BIT:
  758           case FRC_STD_SENSORS_1B:
  759           case FRC_STD_SENSORS_2B:
  760             switch ( sensorIndex )
  761             {
  762               default:
  763                 goto DpaHandleReturnFALSE;
  764 
  765               case 0:
  766                 Carry = Get0_Temperature();
  767                 break;
  768 
  769               case 1:
  770                 Carry = Get1_BinaryData_Light();
  771                 break;
  772 
  773               case 2:
  774                 Carry = Get2_BinaryData_Potentiometer();
  775                 break;
  776             }
  777 
  778             // This type of FRC is not valid for the specified sensor
  779             if ( !Carry )
  780               goto DpaHandleReturnFALSE;
  781 
  782             break;
  783         }
  784 
  785         // Some sensor was measured by FRC, check if there is a sleep request
  786         FSR1++;
  787         if ( INDF1.0 ) // Note: same as PerStdSensorFrc.Options.0
  788         {
  789           // Remember sleep parameters to go to sleep at the Idle event later
  790           PerOSSleep_Request.Time.low8 = FSR1[offsetof( TPerOSSleep_Request, Time ) + 0 + offsetof( TPerStdSensorFrc, SleepParameters ) - 3];
  791           PerOSSleep_Request.Time.high8 = FSR1[offsetof( TPerOSSleep_Request, Time ) + 1 + offsetof( TPerStdSensorFrc, SleepParameters ) - 3];
  792           PerOSSleep_Request.Control = FSR1[offsetof( TPerOSSleep_Request, Control ) + offsetof( TPerStdSensorFrc, SleepParameters ) - 3];
  793         }
  794       }
  795 
  796       break;
  797 
  798       // -------------------------------------------------
  799     case DpaEvent_FrcResponseTime:
  800       // Called to get FRC response time
  801 
  802       // In this example the FRC commands are fast
  803       switch ( DataOutBeforeResponseFRC[0] )
  804       {
  805         case FRC_STD_SENSORS_BIT:
  806         case FRC_STD_SENSORS_1B:
  807         case FRC_STD_SENSORS_2B:
  808           responseFRCvalue = _FRC_RESPONSE_TIME_40_MS;
  809           break;
  810       }
  811       break;
  812   }
  813 DpaHandleReturnFALSE:
  814   return FALSE;
  815 }
  816 
  817 //############################################################################################
  818 bit returnTRUE()
  819 //############################################################################################
  820 {
  821   return TRUE;
  822 }
  823 
  824 //############################################################################################
  825 bit returnFALSE()
  826 //############################################################################################
  827 {
  828   return FALSE;
  829 }
  830 
  831 //############################################################################################
  832 // Increases FSR1 and then stores the byte
  833 void setPlusPlusINDF1( uns8 data @ W )
  834 //############################################################################################
  835 {
  836   FSR1++; // Note: must not modify W
  837   setINDF1( data );
  838 }
  839 
  840 //############################################################################################
  841 // Stores measured sensor value byte(s) and optionally sensor type to the FSR[+1...]
  842 void StoreValue( uns8 sensorType )
  843 //############################################################################################
  844 {
  845   // Is the sensor type to be stored too?
  846   if ( _PCMD == PCMD_STD_SENSORS_READ_TYPES_AND_VALUES )
  847     setPlusPlusINDF1( sensorType );
  848 
  849   // Store lower value byte
  850   setPlusPlusINDF1( sensorValue.low8 );
  851 
  852   // No more value bytes to store?
  853   if ( sensorType.7 != 0 )
  854     return;
  855 
  856   // Store higher value byte
  857   setPlusPlusINDF1( sensorValue.high8 );
  858 
  859   // Note: this example implements sensors returning only 1 or 2 bytes of data. If another data widths are returned, then it must be implemented explicitly.
  860 }
  861 
  862 //############################################################################################
  863 bit setFRCerror()
  864 //############################################################################################
  865 {
  866   responseFRCvalue2B = FRC_STD_FRC_ERROR_VALUE;
  867   return returnTRUE();
  868 }
  869 
  870 //############################################################################################
  871 bit sensorError;
  872 bit AdjustFrcTemperature()
  873 //############################################################################################
  874 {
  875   // Test for supported FRC commands
  876   switch ( _PCMD )
  877   {
  878     default:
  879       return returnFALSE();
  880 
  881     case FRC_STD_SENSORS_1B:
  882       // Return sensor FRC value 1B
  883       // Check for out of limits
  884       if ( sensorError || (int16)sensorValue > (int16)( 105.5 * 16 ) || (int16)sensorValue < ( (int16)-20 * 16 ) )
  885         return setFRCerror();
  886 
  887       // Convert to the "F = ( T + 22 ) * 2 " from 1/16 resolution
  888       responseFRCvalue2B = sensorValue + 4; // Note: do rounding when /8
  889       responseFRCvalue2B /= 8;
  890       responseFRCvalue += 44;
  891       break;
  892 
  893     case FRC_STD_SENSORS_2B:
  894       // Return sensor FRC value 2B
  895       if ( sensorError )
  896         return setFRCerror();
  897 
  898       responseFRCvalue2B = sensorValue ^ 0x8000;
  899       break;
  900   }
  901 
  902   return returnTRUE();
  903 }
  904 
  905 //############################################################################################
  906 // Sensor index 1: measure temperature using one of the DDC-SE-01 sensors
  907 bit Get0_Temperature()
  908 //############################################################################################
  909 {
  910   // Make sure FSR1 is not modified
  911 
  912   // Measure temperature using DDC-SE-01 sensors
  913   // Read temperature and check for an error
  914 
  915   // Reads temperature from an enabled sensor
  916 #ifdef DALLASnotMCP
  917   sensorError = FALSE;
  918   // Temperature is ready at the background
  919   sensorValue = temperature;
  920   // When error, return standard (FRC) error value(s)
  921   if ( STD_SENSOR_TYPE_TEMPERATURE_IS_ERROR( sensorValue ) )
  922     sensorError = TRUE;
  923 #else
  924   sensorError = TRUE;
  925   // Temperature value must be read from I2C sensor
  926   STD_SENSOR_TYPE_TEMPERATURE_SET_ERROR( sensorValue );
  927   // MCP9802 address
  928   _DpaApiI2Cstart( I2C_ADR );
  929   if ( !I2CwasTimeout )
  930   {
  931     // pointer: 1 = configuration register
  932     _DpaApiI2Cwrite( 0x01 );
  933     // configuration: 9-bit ADC
  934     _DpaApiI2CwriteAndStop( 0x00 );
  935 
  936     // MCP9802 address
  937     _DpaApiI2Cstart( I2C_ADR );
  938     // pointer: 0 = temperature
  939     _DpaApiI2CwriteAndStop( 0 );
  940 
  941     // MCP9802 address + read
  942     _DpaApiI2Cstart( I2C_ADR | 1 );
  943     // store the result
  944     sensorValue.high8 = _DpaApiI2Cread( TRUE );
  945     sensorValue.low8 = _DpaApiI2Cread( FALSE );
  946     _DpaApiI2Cstop();
  947 
  948     sensorValue += 0x10 / 2;
  949     sensorValue /= 0x10;
  950 
  951     sensorError = FALSE;
  952   }
  953 #endif
  954 
  955   // FrcValues
  956   return AdjustFrcTemperature();
  957 }
  958 
  959 //############################################################################################
  960 bit Get_BinaryData_Final( uns8 _ADCcfg @ W )
  961 //############################################################################################
  962 {
  963 #if defined( TR7xG )
  964   ADPCH = _ADCcfg;
  965 #else
  966   ADCON0 = _ADCcfg;
  967 #endif
  968 
  969   // Read ADC
  970 
  971 #if defined( TR7xG )
  972   // ADC is enabled, ADGO is cleared upon completion, Clock supplied according to ADCLK register, left-justified
  973   ADCON0 = 0b1000.0000;
  974   // ADC Conversion Clock = FOSC/8
  975   ADCLK = 8 / 2 - 1;
  976 #else
  977   // ADC result - left justified, Fosc/8
  978   ADCON1 = 0b0001.0000;
  979 #endif
  980 
  981   // Short delay to stabilize
  982   updateCRC16( W );
  983 
  984   // start ADC
  985   _GO = 1;
  986   // wait for ADC finish
  987   while ( _GO );
  988   // Get ADC value
  989   sensorValue.low8 = ADRESH / 2;
  990 
  991   // Return sensor FRC value
  992 
  993   // Test for supported FRC commands
  994   switch ( _PCMD )
  995   {
  996     default:
  997       return returnFALSE();
  998 
  999     case FRC_STD_SENSORS_BIT:
 1000       // If there is a sensor error, 2-bit FRC cannot indicate it, it returns [01]
 1001 
 1002       // Number of shifts to get the bit out of the return value
 1003       uns8 bitLoop = ( INDF1 >> 5 ) + 1;
 1004       // Value to get the bit from
 1005       W = sensorValue.low8;
 1006       do
 1007       {
 1008         // Get the bit to Carry
 1009         W = rr( W );
 1010         // Next bit
 1011       } while ( --bitLoop != 0 ); // Note: must not modify W and Carry
 1012       // Carry = bit = x
 1013       // Current (prepared by DPA) FRC value is [01], change it to [1x]
 1014       responseFRCvalue = rl( responseFRCvalue );
 1015       break;
 1016 
 1017     case FRC_STD_SENSORS_1B:
 1018       responseFRCvalue = sensorValue.low8 + 4;
 1019       break;
 1020   }
 1021 
 1022   return returnTRUE();
 1023 }
 1024 
 1025 #if defined( TR7xG )
 1026 //############################################################################################
 1027 // Might not be needed if ADC registers are kept default
 1028 void resetADCregs()
 1029 //############################################################################################
 1030 {
 1031   // Start reseting ADC registers from ADCON0 to ADPCH
 1032   FSR0 = &ADCON0;
 1033   do
 1034   {
 1035     setINDF0( 0 );
 1036     FSR0++;
 1037     // Stop reseting at 1st GPR register, ADPCH is the last implemented register before GPR
 1038   } while ( !FSR0L.5 );
 1039 }
 1040 #endif
 1041 
 1042 //############################################################################################
 1043 // Sensor index 1: returns light intensity indicator value using DDC-SE-01
 1044 bit Get1_BinaryData_Light()
 1045 //############################################################################################
 1046 {
 1047   // Make sure FSR1 is not modified
 1048 
 1049   // ADC initialization (for more info see PIC datasheet) pin C1 (AN0-D/ANA0-G) as analog input
 1050   ANSELA.0 = 1;
 1051 #if defined( TR7xG )
 1052   // Enable ADC
 1053   ADCMD = 0;
 1054   // Resets all ADC registers
 1055   // VREF- is connected to AVSS, VREF+ is connected to VDD
 1056   resetADCregs();
 1057   //  ADC Positive Input Channel = ANA0
 1058   return Get_BinaryData_Final( 0x00 );
 1059 #else
 1060   // ADC setting (AN0 channel)
 1061   return Get_BinaryData_Final( 0b0.00000.01 );
 1062 #endif
 1063 }
 1064 
 1065 //############################################################################################
 1066 // Sensor index 2: returns potentiometer value using DDC-SE-01
 1067 bit Get2_BinaryData_Potentiometer()
 1068 //############################################################################################
 1069 {
 1070   // Make sure FSR1 is not modified
 1071 
 1072   // ADC initialization (for more info see PIC datasheet) pin C5 (AN4-D/ANA5-G) as analog input
 1073   ANSELA.5 = 1;
 1074 #if defined( TR7xG )
 1075   // Enable ADC
 1076   ADCMD = 0;
 1077   // Resets all ADC registers
 1078   // VREF- is connected to AVSS, VREF+ is connected to VDD
 1079   resetADCregs();
 1080   //  ADC Positive Input Channel = ANA5
 1081   return Get_BinaryData_Final( 0x05 );
 1082 #else
 1083   // ADC setting (AN4 channel)
 1084   return Get_BinaryData_Final( 0b0.00100.01 );
 1085 #endif
 1086 }
 1087 
 1088 #ifdef DALLASnotMCP
 1089 //############################################################################################
 1090 // OneWire and Dallas 18B20 routines
 1091 //############################################################################################
 1092 
 1093 //############################################################################################
 1094 void Delay5us( uns8 val @ W ) // Absolutely precise timing but val != 0
 1095 //############################################################################################
 1096 {
 1097   // 16 MHz
 1098   // + 0.75us ( W=val, Call )
 1099   for ( ;; )
 1100   {         // loop time
 1101     nop2(); // 0.50us
 1102     nop2(); // 1.00us
 1103     nop2(); // 1.50us
 1104     nop2(); // 2.00us
 1105     nop2(); // 2.50us
 1106     nop2(); // 3.00us
 1107     nop();  // 3.25us
 1108     if ( --val == 0 ) // + 0.75us (W--, BTFS )
 1109       return;         // + 0.25us
 1110     nop2(); // 4.50us
 1111   }         // 5.00us (Goto)
 1112 }
 1113 //############################################################################################
 1114 
 1115 #define OneWireData0()  { OneWire_TRIS = 0; }     // 0.5us @ 16MHz
 1116 #define OneWireData1()  { OneWire_TRIS = 1; }     // 0.5us @ 16MHz
 1117 
 1118 //############################################################################################
 1119 void OneWireWriteByte( uns8 byte )
 1120 //############################################################################################
 1121 {
 1122   uns8 bitLoop = 8;
 1123   do
 1124   {
 1125     // Next sequence is time precision critical
 1126     GIE = FALSE;
 1127 
 1128     OneWireData0();
 1129     nop2();         // 1 us [0.5 us]
 1130     nop2();         // [1.0 us]
 1131     if ( byte.0 )   // 2.5 us [1.75us]
 1132       OneWireData1();
 1133 
 1134     // End of time precision critical sequence
 1135     GIE = TRUE;
 1136 
 1137     // 60us minimum in total, does not have to be precise
 1138     Delay5us( ( 60 - 3 ) / 5 + 1 );
 1139 
 1140     OneWireData1();
 1141 
 1142     byte >>= 1;
 1143   } while ( --bitLoop != 0 );
 1144 }
 1145 
 1146 //############################################################################################
 1147 uns8 OneWireReadByte()
 1148 //############################################################################################
 1149 {
 1150   uns8 result;
 1151   uns8 bitLoop = 8;
 1152   do
 1153   {
 1154     // Next sequence is time precision critical
 1155     GIE = FALSE;
 1156 
 1157     OneWireData0();
 1158     nop2();         // 1 us [0.5 us]
 1159     nop2();         // [1.0 us]
 1160     OneWireData1();         // 2 us [1.5 us]
 1161     Delay5us( 15 / 5 );     // 17 us [16.5 us]
 1162 
 1163     Carry = 0;              // 17.5 us [16.75 us]
 1164     if ( OneWire_IO_IN )    // 18.5 us [ 17.25 us] (condition must not modify Carry)
 1165       Carry = 1;
 1166 
 1167     // End of time precision critical sequence
 1168     GIE = TRUE;             // must not modify Carry
 1169     result = rr( result );
 1170 
 1171     // 60us minimum in total, does not have to be precise
 1172     Delay5us( ( 60 - 20 ) / 5 + 1 );
 1173   } while ( --bitLoop != 0 );
 1174 
 1175   return result;
 1176 }
 1177 
 1178 //############################################################################################
 1179 bit OneWireReset()
 1180 //############################################################################################
 1181 {
 1182   // Setting the pin once to low is enough
 1183   OneWire_IO_OUT = 0;
 1184   // Reset pulse
 1185   OneWireData0();
 1186   Delay5us( 500 / 5 );
 1187   // Reset pulse end
 1188   OneWireData1();
 1189   // Next sequence is time precision critical
 1190   GIE = FALSE;
 1191   // Wait for presence pulse
 1192   Delay5us( 70 / 5 );
 1193   // End of time precision critical sequence
 1194   GIE = TRUE;
 1195   // Presence pulse?
 1196   if ( OneWire_IO_IN )
 1197   {
 1198     // No presence, finish initialization sequence
 1199     Delay5us( ( 500 - 70 ) / 5 );
 1200     return returnFALSE();
 1201   }
 1202   else
 1203   {
 1204     // Presence OK, finish initialization sequence
 1205     Delay5us( ( 500 - 70 ) / 5 );
 1206     return returnTRUE();
 1207   }
 1208 }
 1209 
 1210 //############################################################################################
 1211 void OneWireCmd( uns8 cmd )
 1212 //############################################################################################
 1213 {
 1214   // OneWire: Skip ROM
 1215   OneWireWriteByte( CMD_SKIPROM );
 1216   // OneWire: Send command
 1217   OneWireWriteByte( cmd );
 1218 }
 1219 
 1220 //############################################################################################
 1221 bit Ds18B20WriteConfig( uns8 value )
 1222 //############################################################################################
 1223 {
 1224   if ( OneWireReset() )
 1225   {
 1226     // Write Scratchpad
 1227     OneWireCmd( CMD_WSCRATCHPAD );
 1228 
 1229     // Write TL = ? (we dot not care the value)
 1230     OneWireWriteByte( W );
 1231     // Write TH = ? (we dot not care the value)
 1232     OneWireWriteByte( W );
 1233     // Write Config byte
 1234     OneWireWriteByte( value );
 1235 
 1236     if ( OneWireReset() )
 1237     {
 1238       //  Copy Scratchpad
 1239       OneWireCmd( CMD_CPYSCRATCHPAD );
 1240       return returnTRUE();
 1241     }
 1242   }
 1243   return returnFALSE();
 1244 }
 1245 
 1246 #endif // DALLASnotMCP
 1247 //############################################################################################
 1248 // Other routines
 1249 //############################################################################################
 1250 
 1251 //############################################################################################
 1252 void SetOutput( uns8 state, uns8 index @ W )
 1253 //############################################################################################
 1254 {
 1255   // Note: FSRs must not be modified
 1256   // Note: This method is called in the interrupt too!
 1257 
 1258   skip( index );
 1259 #pragma computedGoto 1
 1260   goto set0;
 1261   goto set1;
 1262 #pragma computedGoto 0
 1263   ;
 1264   // --------------------------------------
 1265 set1:
 1266   if ( !state.0 )
 1267     RELAY2_LAT = 0;
 1268   else
 1269     RELAY2_LAT = 1;
 1270 
 1271   return;
 1272   // --------------------------------------
 1273 set0:
 1274   if ( !state.0 )
 1275     RELAY1_LAT = 0;
 1276   else
 1277     RELAY1_LAT = 1;
 1278 
 1279   return;
 1280   // --------------------------------------
 1281 }
 1282 
 1283 //############################################################################################
 1284 bit GetOutput( uns8 index @ W )
 1285 //############################################################################################
 1286 {
 1287   Carry = FALSE; // Note: must not modify W
 1288 
 1289   // Note: all below must not modify Carry except when needed
 1290   skip( index );
 1291 #pragma computedGoto 1
 1292   goto get0;
 1293   goto get1;
 1294 #pragma computedGoto 0
 1295   ;
 1296   // --------------------------------------
 1297 get1:
 1298   if ( RELAY2_LAT )
 1299     Carry = TRUE;
 1300   goto _return;
 1301   // --------------------------------------
 1302 get0:
 1303   if ( RELAY1_LAT )
 1304     Carry = TRUE;
 1305   goto _return;
 1306   // --------------------------------------
 1307 
 1308 _return:
 1309   return Carry;
 1310 }
 1311 
 1312 //############################################################################################
 1313 // Default Custom DPA Handler header; 2nd include implementing a Code bumper to detect too long code of the Custom DPA Handler (modify the path according to your setup)
 1314 #include "DPAcustomHandler.h"
 1315 //############################################################################################