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