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