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