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