1 // *********************************************************************
    2 //   Custom DPA Handler code example - User peripheral implementation  *
    3 // *********************************************************************
    4 //
    5 // File:    $RCSfile: CustomDpaHandler-StreetLighting.c,v $
    6 // Version: $Revision: 1.37 $
    7 // Date:    $Date: 2021/11/05 10:25:14 $
    8 //
    9 // Revision history:
   10 //   2017/03/13  Release for DPA 3.00
   11 //   2016/09/27  Release for DPA 2.28
   12 //   2015/08/05  Release for DPA 2.20
   13 //   2014/10/31  Release for DPA 2.10
   14 //
   15 // *********************************************************************
   16 
   17 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/
   18 
   19 // This example implements DPA custom handler for Street Lighting application according to the specification HWP-StreetLightingSpecification.pdf
   20 // This example works only at STD mode, not at LP mode
   21 
   22 // Default IQRF include (modify the path according to your setup)
   23 #include "IQRF.h"
   24 
   25 // Default DPA header (modify the path according to your setup)
   26 #include "DPA.h"
   27 // Default Custom DPA Handler header (modify the path according to your setup)
   28 #include "DPAcustomHandler.h"
   29 
   30 // Light is controlled via an IO PIN. Choose requested pin or use internal LED.
   31 //#define LIGHT_TRIS            _C6_TRIS
   32 //#define LIGHT_PIN             _C6_OUT
   33 
   34 // Internal LEDG is used (LIGHT_TRIS def. not needed)
   35 #define LIGHT_PIN               _LEDG
   36 
   37 // Internal LEDR is used (LIGHT_TRIS def. not needed)
   38 //#define LIGHT_PIN             _LEDR
   39 
   40 // Check the LIGHT_PIN is defined
   41 #ifndef LIGHT_PIN
   42 #error Please define IO PIN controlling the Light
   43 #endif
   44 
   45 // Street Lighting peripheral
   46 #define PNUM_STREET_LIGHT       PNUM_USER
   47 #define HWPID_STREET_LIGHT      0x0021
   48 
   49 // Peripheral implemented commands
   50 #define PCMD_SET_LIGHT          0x00    // Direct light control
   51 #define PCMD_SET_ZONE           0x01    // Set light zone
   52 #define PCMD_SET_SCHEDULE       0x02    // Set and execute the light schedule
   53 #define PCMD_SEND_DATA          0x03    // Exchange general data
   54 
   55 // FRC commands
   56 #define FRC_GET_STATE           ( FRC_USER_BIT_FROM + 0 )   // FRC bit command - get the light state
   57 #define FRC_GET_WARN            ( FRC_USER_BIT_FROM + 1 )   // FRC bit command - get the light warning
   58 #define FRC_SET_INTENS          ( FRC_USER_BIT_FROM + 2 )   // FRC bit command - set intensity of the light, return warning information
   59 #define FRC_SET_INTERV          ( FRC_USER_BIT_FROM + 3 )   // FRC bit command - set interval, return warning information
   60 #define FRC_GET_SCHED_STAT      ( FRC_USER_BIT_FROM + 4 )   // FRC bit command - get scheduler status
   61 #define FRC_GET_INTENS          ( FRC_USER_BYTE_FROM + 0 )  // FRC byte command - get the light intensity
   62 #define FRC_GET_ZONE            ( FRC_USER_BYTE_FROM + 1 )  // FRC byte command - get the light zone
   63 #define FRC_GET_TEMPER          ( FRC_USER_BYTE_FROM + 2 )  // FRC byte command - get the light temperature
   64 #define FRC_GET_ENRG_CONS       ( FRC_USER_BYTE_FROM + 3 )  // FRC byte command - get energy consumption of the light
   65 #define FRC_GET_RUN_HRS         ( FRC_USER_BYTE_FROM + 4 )  // FRC byte command - get run hours of the light
   66 
   67 // Scheduler definitions
   68 #define SCH_CMD_JUMP            0x00    // Jump to interval specified by Param
   69 #define SCH_CMD_REPEAT          0xff    // Repeat previous interval, number of repetition specified by Param
   70 #define SCH_MAX_INDEX           7       // 8 schedule intervals supported in this example, index 0-7
   71 #define SCH_INTERV_SIZE         sizeof( TSchedInterval )    // Interval size is 2 bytes
   72 #define SCH_MAX_LEN             ( SCH_MAX_INDEX + 1 ) * SCH_INTERV_SIZE // 8 intervals = 16 bytes
   73 #define SCH_ONE_HOUR            3600
   74 #define SCH_DEF_PERIOD          60      // Default schedule period is 1 minute
   75 #define TICK_1_SEC              100
   76 #define TICK_10_MS              10
   77 #define SLEEP_TIME              600     // Sleep time is 10 minutes
   78 
   79 // Node eeprom map
   80 #define EEP_SCHED_PER_L8        0
   81 #define EEP_SCHED_PER_H8        1
   82 
   83 // Filter definitions
   84 #define FLT_ZONE_LEN            1       // Zone filter - 1 byte
   85 #define FLT_BITMAP_LEN          31  // Network bitmap filter - 31 bytes
   86 
   87 // Initialize schedule period to 60 s during programming
   88 #pragma cdata[ __EESTART + PERIPHERAL_EEPROM_START ] = SCH_DEF_PERIOD, 0x00
   89 
   90 // Schedule interval structure
   91 typedef struct
   92 {
   93   uns8 Cmd;             // Command
   94   uns8 Param;           // Parameter
   95 }TSchedInterval;
   96 
   97 // Global variables
   98 uns8 zoneNr;            // Actual device Zone number
   99 uns8 intensity;     // Actual light intensity
  100 
  101 // Prototypes
  102 bit checkNTWbitmap();
  103 bit checkZoneNr();
  104 
  105 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
  106 //############################################################################################
  107 bit CustomDpaHandler()
  108 //############################################################################################
  109 {
  110   // Handler presence mark
  111   clrwdt();
  112 
  113   static uns16 secCounter, runCounter, sleepTimer;
  114   static uns8 tick, PWMtick;
  115   static bit buttonTrigger;
  116   static bit isSchedulerRunning;    // Schedule status
  117   static bit scheduleExecute;           // Execute schedule interval
  118   static bit scheduleStart;             // Start schedule in DpaEvent_AfterRouting [Sync]
  119   static uns16 schedulePeriod;          // Schedule period [s]
  120   static uns8 scheduleRepCount;     // Schedule repetition counter
  121   static uns8 scheduleRepInterv;    // Schedule interval repetition count
  122   static uns8 scheduleInterval;     // Active interval of the scheduler
  123   static uns8 scheduleTimer;            // Scheduler timer
  124   static uns24 energyCons;              // Energy consumption
  125   static uns24 runHours;                    // Run hours
  126   static uns8 prevZoneNr;                   // Actual and previous device Zone number
  127   static uns8 prevIntensity;            // Actual and previous light intensity
  128   static TSchedInterval scheduler[SCH_MAX_INDEX + 1];   // Schedule, 8 interval supported in this example
  129 
  130   // Detect DPA event to handle
  131   switch ( GetDpaEvent() )
  132   {
  133 
  134     // -------------------------------------------------
  135     case DpaEvent_Interrupt:
  136       // Do an extra quick background interrupt work
  137       // ! 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.
  138       // ! 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.
  139       // ! 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.
  140       // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy.
  141       // ! Make sure race condition does not occur when accessing those variables at other places.
  142       // ! 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.
  143       // ! Do not call any OS functions except setINDFx().
  144       // ! Do not use any OS variables especially for writing access.
  145       // ! 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.
  146 
  147 
  148       // TMR6 interrupt (period 1 ms) occurred ?
  149       if ( TMR6IF )
  150       {
  151         // Unmask interrupt
  152         TMR6IF = 0;
  153 
  154         // Set RGB LED
  155         LIGHT_PIN = intensity > PWMtick;
  156 
  157         // PWM 100 Hz
  158         if ( ++PWMtick >= TICK_10_MS )
  159         {
  160           // 10ms
  161           PWMtick = 0;
  162 
  163           // 1 sec
  164           if ( ++tick >= TICK_1_SEC )
  165           {
  166             tick = 0;
  167 
  168             // Count the Light run hours
  169             if ( intensity != 0 )
  170             {
  171               // Light is on for one hour (3600 s) ?
  172               if ( --runCounter == 0 )
  173               {
  174                 //Preset runCounter, increment runHours
  175                 runCounter = SCH_ONE_HOUR;
  176 #ifdef __CC5XFREE__
  177                 W = 1;
  178                 runHours.low8 += W;
  179                 W = 0;
  180                 runHours.mid8 = addWFC( runHours.mid8 );
  181                 runHours.high8 = addWFC( runHours.high8 );
  182 #else
  183                 runHours++;
  184 #endif
  185               }
  186             }
  187 
  188             // Schedule execution
  189             if ( scheduleTimer != 0 )
  190             {
  191               if ( --secCounter == 0 )
  192               {
  193                 secCounter = schedulePeriod;
  194                 // Countdown schedule timer
  195                 if ( --scheduleTimer == 0 )
  196                 {
  197                   if ( isSchedulerRunning )
  198                   {
  199                     // Execute schedule interval in DpaEvent_Idle
  200                     scheduleExecute = TRUE;
  201                   }
  202                   else
  203                   {
  204                     // Schedule isn't running, scheduleTimer is set by FRC Set interval
  205                     // Time elapsed - set previous intensity
  206                     intensity = prevIntensity;
  207                   }
  208                 }
  209               }
  210             }
  211 
  212             //Inc. sleepTimer
  213             sleepTimer++;
  214           }
  215         }
  216       }
  217 
  218 returnTRUE:
  219       return TRUE;
  220 
  221       // -------------------------------------------------
  222     case DpaEvent_Idle:
  223       // Execute the Light scheduler
  224       if ( scheduleExecute )
  225       {
  226         // 8 schedule intervals supported in this example
  227         if ( scheduleInterval > SCH_MAX_INDEX )
  228           scheduleInterval = 0; // scheduleInterval out of range 0-7, start from interval 0
  229 
  230         // Get actual schedule interval command and parameter
  231         FSR0 = (uns16)&scheduler[scheduleInterval];
  232         param3.low8 = FSR0[0];
  233         param3.high8 = FSR0[1];
  234 
  235         // Jump command ?
  236         if ( param3.low8 == SCH_CMD_JUMP )
  237         {
  238           // Jump to specified interval during next pass through DpaEvent_Idle event
  239           scheduleInterval = param3.high8;
  240         }
  241         else
  242         {
  243           // Repeat command ?
  244           if ( param3.low8 == SCH_CMD_REPEAT )
  245           {
  246             // Get number of repetition and number of previous intervals to repeat
  247             if ( scheduleRepCount == 0 )
  248             {
  249               // Number of repetition is specified by bits 0-2
  250               scheduleRepCount = param3.high8 & 0x07;
  251               scheduleRepCount++;
  252               // Number of repeated blocks is specified by bits 3-7
  253               scheduleRepInterv = param3.high8;
  254               scheduleRepInterv = lsr( scheduleRepInterv );
  255               scheduleRepInterv = lsr( scheduleRepInterv );
  256               scheduleRepInterv = lsr( scheduleRepInterv );
  257               scheduleRepInterv++;
  258               goto REPEAT;
  259             }
  260             else
  261             {
  262               // All previous intervals repeated, repeat again ?
  263               if ( --scheduleRepCount != 0 )
  264               {
  265                 // Execute the first repeated interval during next pass through DpaEvent_Idle event
  266 REPEAT:
  267                 scheduleInterval -= scheduleRepInterv;
  268               }
  269               else
  270               {
  271                 // Repetition is finished, skip repeat command, execute next interval
  272                 scheduleInterval++;
  273               }
  274             }
  275           }
  276           else
  277           {
  278             // Set Light intensity specified by current schedule interval
  279             prevIntensity = intensity;
  280             intensity = param3.high8;
  281             scheduleTimer = param3.low8;
  282             // Increase interval index
  283             scheduleInterval++;
  284             // Interval executed
  285             scheduleExecute = FALSE;
  286           }
  287         }
  288       }
  289 
  290       // Go sleep after ~10 min. (= 10 * 60 s) since last peripheral command reception
  291       if ( sleepTimer == SLEEP_TIME )
  292       {
  293         // Stop TMR6, turn off the light
  294 GO_SLEEP:
  295         TMR6IE = FALSE;
  296         TMR6ON = FALSE;
  297         LIGHT_PIN = 0;
  298         // Prepare OS Sleep DPA Request
  299         // Endless sleep
  300         sleepTimer = 0x00;
  301         _PNUM = PNUM_OS;
  302         _PCMD = CMD_OS_SLEEP;
  303         _DpaDataLength = sizeof( _DpaMessage.PerOSSleep_Request );
  304         // Wake up on PIN change, LEDG flash after wake up
  305         _DpaMessage.PerOSSleep_Request.Time = 0x0000;
  306         _DpaMessage.PerOSSleep_Request.Control = 0b101;
  307         // Perform local DPA Request
  308         // BeforeSleep and AfterSleep events will not be called in this case!
  309         DpaApiLocalRequest();
  310         TMR6ON = TRUE;
  311         TMR6IE = TRUE;
  312       }
  313       goto returnFALSE;
  314 
  315       // -------------------------------------------------
  316     case DpaEvent_DpaRequest:
  317       // Called to interpret DPA request for peripherals
  318       // -------------------------------------------------
  319       // Peripheral enumeration
  320       if ( IsDpaEnumPeripheralsRequest() )
  321       {
  322         // We implement 1 user peripheral PNUM = 0x20, HwProfile = 0x0021
  323         _DpaMessage.EnumPeripheralsAnswer.UserPerNr |=  1;
  324         FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_USER + 0 );
  325         _DpaMessage.EnumPeripheralsAnswer.HWPID |= HWPID_STREET_LIGHT;
  326         _DpaMessage.EnumPeripheralsAnswer.HWPIDver |= 0x0100;
  327         goto returnTRUE;
  328       }
  329       // -------------------------------------------------
  330       // Get information about peripheral
  331       else if ( IsDpaPeripheralInfoRequest() )
  332       {
  333         if ( _PNUM == PNUM_STREET_LIGHT )
  334         {
  335           _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_USER_AREA;
  336           _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ_WRITE;
  337           goto returnTRUE;
  338         }
  339         break;
  340       }
  341       // -------------------------------------------------
  342       else
  343       {
  344         // Handle Street lighting peripheral
  345         if ( _PNUM == PNUM_STREET_LIGHT )
  346         {
  347           uns8 _PCMDatW @ W;
  348 
  349           _PCMDatW = _PCMD;
  350           _PCMDatW = PCMD_SEND_DATA - _PCMDatW;
  351           if ( !Carry )
  352             goto UserErrorPCMD;
  353 
  354           skip( _PCMDatW );   // Reverse order
  355 #pragma computedGoto 1
  356           goto _PCMD_SEND_DATA;         //          0x03
  357           goto _PCMD_SET_SCHEDULE;  //      0x02
  358           goto _PCMD_SET_ZONE;          //          0x01
  359           goto _PCMD_SET_LIGHT;         //          0x00
  360 #pragma computedGoto 0
  361           ;
  362           // Direct light control command
  363 _PCMD_SET_LIGHT:
  364           // Zone number filter (1 byte zone + 1 byte intensity) ?
  365           if ( _DpaDataLength == ( FLT_ZONE_LEN + 1 ) )
  366           {
  367             // Check device zone number
  368             if ( checkZoneNr() )
  369             {
  370               // Zone number ok, set intensity of the light
  371               intensity = _DpaMessage.Request.PData[1];
  372               goto SET_INTENS;
  373             }
  374             else
  375               goto PDATA_ERR;       // Incorrect zone number, return PDATA_ERROR
  376           }
  377 
  378           // Network bitmap filter (31 byte bitmap filter + 1 byte intensity) ?
  379           if ( _DpaDataLength == ( FLT_BITMAP_LEN + 1 ) )
  380           {
  381             // The first byte of Network bitmap filter must be 0x00
  382             if ( _DpaMessage.Request.PData[0] != 0x00 )
  383               goto PDATA_ERR;
  384 
  385             // Check the network bitmap
  386             if ( checkNTWbitmap() )
  387             {
  388               // Network bitmap OK, set intensity of the light, stop scheduler
  389               intensity = _DpaMessage.Request.PData[31];
  390 SET_INTENS: _DpaMessage.Response.PData[0] = prevIntensity;
  391               prevIntensity = intensity;
  392               isSchedulerRunning = FALSE;
  393               scheduleTimer = 0;
  394               // Return 1 byte - prevIntensity
  395               goto RESP_ONE_BYTE;
  396             }
  397             else
  398               goto PDATA_ERR;       // Bit for device ntwADDR in the network bitmap is cleared, return data error
  399           }
  400           else
  401             goto PLEN_ERR;          // Incorrect data length
  402 
  403           // Set zone command
  404 _PCMD_SET_ZONE:
  405           // Network bitmap filter (1 byte new zone value + 30 byte Network bitmap filter) ?
  406           if ( _DpaDataLength == FLT_BITMAP_LEN )
  407           {
  408             // Check the network bitmap
  409             if ( checkNTWbitmap() )
  410             {
  411               // Network bitmap ok, check the new zone number value
  412               param3.low8 = _DpaMessage.Request.PData[0];
  413               if ( Zero_ )
  414                 goto PDATA_ERR;             // New zone == 0, return data error
  415               do
  416                 param3.low8 = lsr( param3.low8 );
  417               while ( !Carry );
  418               if ( !Zero_ )
  419                 goto PDATA_ERR;             // New zone - more than one bit is set, return data error
  420               // Ok, set zone, return previous zone
  421               zoneNr = W;
  422               _DpaMessage.Response.PData[0] = prevZoneNr;
  423               prevZoneNr = zoneNr;
  424               // Return one byte - prevZoneNr
  425 RESP_ONE_BYTE:
  426               _DpaDataLength = 0x01;
  427               goto CMD_OK;
  428             }
  429             else
  430               goto PDATA_ERR;       // Bit for device ntwADDR in the network bitmap is cleared, return data error
  431           }
  432           else
  433             goto PLEN_ERR;          // Incorrect data length
  434 
  435           // Set schedule command (max. 8 intervals currently supported)
  436 _PCMD_SET_SCHEDULE:
  437           // Minimal PLEN is 3 byte (zone number + 1 sched. interval = 3 bytes ), PLEN must be an odd number
  438           if ( ( _DpaDataLength < ( FLT_ZONE_LEN + SCH_INTERV_SIZE ) ) || ( _DpaDataLength.0 == 0 ) )
  439             goto PLEN_ERR;
  440 
  441           // Zone number filter ?
  442           if ( _DpaMessage.Request.PData[0] != 0x00 )
  443           {
  444             // Check device zone number
  445             if ( checkZoneNr() )
  446             {
  447               // Zone number ok
  448               // Subtract size of filter
  449               _DpaDataLength -= FLT_ZONE_LEN;
  450               // 8 intervals supported in this example
  451               if ( _DpaDataLength > SCH_MAX_LEN )
  452                 goto PLEN_ERR;
  453               // Set schedule intervals
  454               param3.low8 = FLT_ZONE_LEN;
  455               // Run schedule
  456               goto RUN_SCHEDULE;
  457             }
  458             else
  459               goto PDATA_ERR;       // Incorrect zone number, return PDATA_ERROR
  460           }
  461           else
  462           {
  463             // Minimal PLEN is 33 (Network bitmap filter (31B) + 1 sched. interval (2B) = 33 bytes)
  464             if ( _DpaDataLength < ( FLT_BITMAP_LEN + SCH_INTERV_SIZE ) )
  465               goto PLEN_ERR;        // Incorrect data length
  466             // Check the network bitmap
  467             if ( checkNTWbitmap() )
  468             {
  469               // Network bitmap ok
  470               // Subtract size of filter
  471               _DpaDataLength -= FLT_BITMAP_LEN;
  472               // 8 intervals supported in this example
  473               if ( _DpaDataLength > SCH_MAX_LEN )
  474                 goto PDATA_ERR;
  475               param3.low8 = FLT_BITMAP_LEN;
  476               // Set schedule intervals
  477 RUN_SCHEDULE:
  478               copyMemoryBlock( _DpaMessage.Request.PData + param3.low8, scheduler, _DpaDataLength );
  479               // Run scheduler (in DpaEvent_AfterRouting [Sync])
  480               scheduleStart = TRUE;
  481               // No data is returned
  482               _DpaDataLength = 0x00;
  483               // Return TRUE to indicate peripheral was handled
  484               goto CMD_OK;
  485             }
  486             else
  487               goto PDATA_ERR;       // Bit for device ntwADDR in the network bitmap filter is cleared, return data error
  488           }
  489 
  490           // Exchange general data command
  491 _PCMD_SEND_DATA:
  492           // Minimal PLEN is 1 (zone number)
  493           if ( _DpaDataLength == 0x00 )
  494             goto PLEN_ERR;
  495 
  496           // Zone number filter ?
  497           if ( _DpaMessage.Request.PData[0] != 0x00 )
  498           {
  499             // Check device zone number
  500             if ( checkZoneNr() )
  501             {
  502               // Zone number OK
  503               // Application specific data processing
  504               // ...
  505               // ...
  506               // ...
  507               // Set schedule period, complete schedule and period is returned in this example
  508               param3.low8 = _DpaMessage.Request.PData[FLT_ZONE_LEN];
  509               param3.high8 = _DpaMessage.Request.PData[FLT_ZONE_LEN + 1];
  510               goto SET_SCH_PER;
  511             }
  512             else
  513               goto PDATA_ERR;       // Incorrect zone number, return PDATA_ERROR
  514           }
  515           else
  516           {
  517             // Network bitmap filter
  518             if ( _DpaDataLength < FLT_BITMAP_LEN )
  519             {
  520               // Incorrect data length
  521 PLEN_ERR:
  522               W = ERROR_DATA_LEN;
  523               goto UserErrorW;
  524             }
  525 
  526             // Check the network bitmap
  527             if ( checkNTWbitmap() )
  528             {
  529               // Network bitmap OK
  530               // Application specific data processing
  531               // ...
  532               // ...
  533               // ...
  534               // Set schedule period, complete schedule and period is returned in this example
  535               param3.low8 = _DpaMessage.Request.PData[FLT_BITMAP_LEN];
  536               param3.high8 = _DpaMessage.Request.PData[FLT_BITMAP_LEN + 1];
  537 SET_SCH_PER:
  538               if ( ( param3 == 0 ) || ( param3 > SCH_ONE_HOUR ) )
  539                 goto PDATA_ERR;
  540 
  541               schedulePeriod.low8 = param3.low8;
  542               schedulePeriod.high8 = param3.high8;
  543               eeWriteByte( EEP_SCHED_PER_L8, param3.low8 );
  544               eeWriteByte( EEP_SCHED_PER_H8, param3.high8 );
  545               copyMemoryBlock( scheduler, _DpaMessage.Response.PData, SCH_MAX_LEN );
  546               _DpaMessage.Response.PData[SCH_MAX_LEN] = schedulePeriod.low8;
  547               _DpaMessage.Response.PData[SCH_MAX_LEN + 1] = schedulePeriod.high8;
  548               _DpaDataLength = SCH_MAX_LEN + 2;
  549               // If the schedule is running, restart it with new period
  550               if ( isSchedulerRunning )
  551                 scheduleStart = TRUE;
  552 CMD_OK:
  553               sleepTimer = 0;
  554               goto returnTRUE;
  555             }
  556             else
  557             {
  558               // Bit for device ntwADDR in the network bitmap filter is cleared, return data error
  559 PDATA_ERR:
  560               W = ERROR_DATA;
  561               goto UserErrorW;
  562             }
  563 
  564             // Invalid PCmd error
  565 UserErrorPCMD:
  566             W = ERROR_PCMD;
  567             // User error - err code in W reg
  568 UserErrorW:
  569             _DpaMessage.ErrorAnswer.ErrN = W;
  570             _DpaMessage.ErrorAnswer.PNUMoriginal = _PNUM;
  571             _PNUM = PNUM_ERROR_FLAG;
  572             _DpaDataLength = sizeof( _DpaMessage.ErrorAnswer );
  573             goto returnTRUE;
  574           }
  575         }
  576       }
  577       break;
  578 
  579       // -------------------------------------------------
  580     case DpaEvent_FrcValue:
  581       // Called to get FRC value
  582       // Process the FRC
  583       switch ( _PCMD )
  584       {
  585         //FRC bit command - set intensity of the light, return warning information
  586         case FRC_SET_INTENS:
  587           // Check device zone number
  588           if ( DataOutBeforeResponseFRC[0] & zoneNr )
  589           {
  590             // Zone number OK, set intensity
  591             intensity = DataOutBeforeResponseFRC[1];
  592             prevIntensity = intensity;
  593           }
  594           // Light state is returned in this example
  595           goto RET_STATE;
  596 
  597           // FRC bit command - set interval, return warning information
  598         case FRC_SET_INTERV:
  599           // Check interval time
  600           if ( ( DataOutBeforeResponseFRC[0] != 0x00 ) && ( DataOutBeforeResponseFRC[0] != 0xff ) )
  601           {
  602             // Interval OK, stop scheduler, set requested light intensity
  603             intensity = DataOutBeforeResponseFRC[1];
  604             isSchedulerRunning = FALSE;
  605 
  606             tick = 0;
  607             secCounter = schedulePeriod;
  608             scheduleTimer = DataOutBeforeResponseFRC[0];
  609           }
  610           // Light state is returned in this example
  611 
  612           // FRC bit command - get the light warning
  613         case FRC_GET_WARN:
  614           // Bit.0 bit.1
  615           //   0     0 - Device didn't respond
  616           //   0     1 - Warning (HW failure, high temperature, etc.)
  617           //     1     0 - Light is off
  618           //     1     1 - Light is on (intensity > 0x00)
  619           // Light state is returned in this example
  620 
  621           // FRC bit command - get the light state
  622         case FRC_GET_STATE:
  623           // Bit.0 is always 1, bit.1 is set when the light intensity > 0x00
  624 RET_STATE:
  625           if ( intensity != 0x00 )
  626             goto FRCvalueBit1AndSleep;
  627           goto CLR_SLEEP;
  628 
  629           // FRC bit command - get scheduler status
  630         case FRC_GET_SCHED_STAT:
  631           // Bit.0 is always 1, bit.1 is set when the schedule is running
  632           if ( isSchedulerRunning )
  633           {
  634 FRCvalueBit1AndSleep:
  635             responseFRCvalue.1 = TRUE;
  636           }
  637           goto CLR_SLEEP;
  638 
  639           // FRC byte command - get the light intensity
  640         case FRC_GET_INTENS:
  641           responseFRCvalue = intensity;
  642           if ( intensity != 0xff )
  643             responseFRCvalue++;
  644           goto CLR_SLEEP;
  645 
  646           // FRC byte command - get the light zone
  647         case FRC_GET_ZONE:
  648           responseFRCvalue = zoneNr;
  649           goto CLR_SLEEP;
  650 
  651           // FRC byte command - get the light temperature
  652         case FRC_GET_TEMPER:
  653           responseFRCvalue = getTemperature();
  654           // In case of 0 deg. C fixed value 0x7f is returned
  655           if ( responseFRCvalue == 0x00 )
  656             responseFRCvalue = 0x7f;
  657           goto CLR_SLEEP;
  658 
  659           // FRC byte command - get energy consumption of the light
  660         case FRC_GET_ENRG_CONS:
  661           FSR0 = (uns16)&energyCons;            // Measurement of consumed energy is application specific
  662           goto RET_21bit;
  663 
  664           // FRC byte command - get run hours of the light
  665         case FRC_GET_RUN_HRS:
  666           FSR0 = (uns16)&runHours;
  667 RET_21bit:
  668           // Bits 0-6 is returned if DataOutBeforeResponseFRC[0] == 0x00
  669           if ( DataOutBeforeResponseFRC[0] == 0x00 )
  670             responseFRCvalue = FSR0[0];
  671           else
  672           {
  673             // Bits 7-13 is returned if DataOutBeforeResponseFRC[0] == 0x00
  674             if ( DataOutBeforeResponseFRC[0] == 0x01 )
  675             {
  676               param3.low8 = FSR0[0];
  677               responseFRCvalue = FSR0[1];
  678               goto ROLL;
  679             }
  680             else
  681             {
  682               // Bits 14-21 is returned if DataOutBeforeResponseFRC[0] >= 0x02
  683               param3.low8 = FSR0[1];
  684               responseFRCvalue = FSR0[2];
  685               param3.low8 = rl( param3.low8 );
  686               responseFRCvalue = rl( responseFRCvalue );
  687 ROLL:
  688               param3.low8 = rl( param3.low8 );
  689               responseFRCvalue = rl( responseFRCvalue );
  690             }
  691           }
  692 
  693           // Clear MSB, add 1
  694           responseFRCvalue.7 = 0;
  695           responseFRCvalue++;
  696 CLR_SLEEP:
  697           sleepTimer = 0x00;
  698           break;
  699       }
  700       break;
  701 
  702       // -------------------------------------------------
  703     case DpaEvent_AfterRouting:
  704       // Called after Notification and after routing of the DPA response was finished
  705       if ( scheduleStart )
  706       {
  707         //Sync start of light scheduler
  708         scheduleStart = FALSE;
  709         scheduleInterval = 0x00;
  710         secCounter = schedulePeriod;
  711         tick = 0;
  712         scheduleRepCount = 0x00;
  713         isSchedulerRunning = TRUE;
  714         scheduleExecute = TRUE;
  715       }
  716       break;
  717 
  718       // -------------------------------------------------
  719     case DpaEvent_Init:
  720       // Initialization
  721       LIGHT_PIN = 0;
  722 #ifdef LIGHT_TRIS
  723       LIGHT_TRIS = 0;
  724 #endif
  725       W = 1;
  726       zoneNr = W;
  727       prevZoneNr = W;
  728       runHours = 0;
  729       energyCons = 0;
  730       runCounter = SCH_ONE_HOUR;
  731       isSchedulerRunning = FALSE;
  732       scheduleStart = FALSE;
  733       scheduleExecute = FALSE;
  734       scheduleTimer = 0;
  735       tick = 0;
  736       // Read schedule timer
  737       schedulePeriod.low8 = eeReadByte( EEP_SCHED_PER_L8 );
  738       schedulePeriod.high8 = eeReadByte( EEP_SCHED_PER_H8 );
  739       // Setup TMR6 to generate PWM for LED switching
  740 
  741 #if F_OSC == 16000000
  742       PR6 = 250 - 1;
  743       T6CON = 0b0.0000.1.10;    // Prescaler 16, Postscaler 1, 1 ms interrupt
  744 #else
  745 #error Unsupported oscillator frequency
  746 #endif
  747 
  748       TMR6IE = TRUE;
  749       break;
  750 
  751       // -------------------------------------------------
  752     case DpaEvent_AfterSleep:
  753       // Called on wake-up from sleep
  754       TMR6IE = TRUE;
  755       TMR6ON = TRUE;
  756       break;
  757 
  758       // -------------------------------------------------
  759     case DpaEvent_BeforeSleep:
  760       // Called before going to sleep   (the same handling as DpaEvent_DisableInterrupts event)
  761 
  762       // -------------------------------------------------
  763     case DpaEvent_DisableInterrupts:
  764       // Called when device needs all hardware interrupts to be disabled (before Reset, Restart, LoadCode, Remove bond, and Run RFPGM)
  765       // Must not use TMR6 any more
  766       TMR6ON = FALSE;
  767       TMR6IE = FALSE;
  768       break;
  769   }
  770 
  771 returnFALSE:
  772   return FALSE;
  773 }
  774 
  775 //############################################################################################
  776 // Check device zone number
  777 bit checkZoneNr()
  778 //############################################################################################
  779 {
  780   W = _DpaMessage.Request.PData[0] & zoneNr;
  781   return W != 0;
  782 }
  783 
  784 //############################################################################################
  785 // Check the network bit map
  786 bit checkNTWbitmap()
  787 //############################################################################################
  788 {
  789   // Get index of _DpaMessage.Request.PData containing 8-bit filter for device ntwADDR
  790   param3.low8 = ntwADDR;
  791   param3.high8 = param3.low8 % 8 + 1;   // param3.high8 = (ntwADDR % 8) + 1
  792   param3.low8 = lsr( param3.low8 );
  793   param3.low8 = lsr( param3.low8 );
  794   param3.low8 = lsr( param3.low8 );
  795   FSR0 = _DpaMessage.Request.PData + param3.low8 + 1;
  796   // param3.low8 = Current byte of network bitmap
  797   param3.low8 = *FSR0;
  798 
  799   do
  800   {
  801     param3.low8 = lsr( param3.low8 );
  802   } while ( --param3.high8 != 0 );
  803 
  804   return Carry;
  805 }
  806 
  807 //############################################################################################
  808 // Default Custom DPA Handler header; 2nd include implementing a Code bumper to detect too long code of the Custom DPA Handler (modify the path according to your setup)
  809 #include "DPAcustomHandler.h"
  810 //############################################################################################@
  811