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