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