1 // *********************************************************************
    2 //   Custom DPA Handler code example - Multiple response example       *
    3 // *********************************************************************
    4 // Copyright (c) MICRORISC s.r.o.
    5 //
    6 // File:    $RCSfile: CustomDpaHandler-MultiResponse.c,v $
    7 // Version: $Revision: 1.35 $
    8 // Date:    $Date: 2021/04/26 15:13:50 $
    9 //
   10 // Revision history:
   11 //   2020/09/03  Release for DPA 4.15
   12 //   2019/01/10  Release for DPA 4.00
   13 //   2017/03/13  Release for DPA 3.00
   14 //
   15 // *********************************************************************
   16 
   17 // Online DPA documentation https://doc.iqrf.org/DpaTechGuide/
   18 
   19 // This is an !EXPERIMENTAL EXAMPLE! that shows how to receive responses from more nodes to the one request.
   20 // This example works only at STD mode.
   21 // Because of used uns24 type it requires an extended version of CC5X compiler.
   22 
   23 // The example allows to send DPA request to multiple selected nodes (addressed by VRN but NOT by logical address). Addressed nodes then send one by one their responses.
   24 // Please see TRequest structure below. Just broadcast this structure at PDATA top the PNUM=0x20 and PCMD=0x00.
   25 // Only addressed, discovered and routing nodes will respond.
   26 
   27 // Default IQRF include (modify the path according to your setup)
   28 #include "IQRF.h"
   29 // Default DPA header (modify the path according to your setup)
   30 #include "DPA.h"
   31 // Default Custom DPA Handler header (modify the path according to your setup)
   32 #include "DPAcustomHandler.h"
   33 
   34 //############################################################################################
   35 
   36 typedef struct
   37 {
   38   // Length of the response slot in 10 ms units. The length must be big enough to fit the longest response from any addressed node.
   39   // Please see MIN_TIMESLOT and MAX_TIMESLOT.
   40   uns8  ResponseSlot;
   41   // Bitmap of the addressed nodes. Each bit represent one node. VrnBitmap[0].1 is VRN=1, VrnBitmap[0].2 is VRN=2, ..., VrnBitmap[29].7 is VRN=239
   42   // Mapping from logical address to VRN is stored at Coordinator external EEPROM table at address 0x5000. See IQRF OS documentation for more information.
   43   uns8  VrnBitmap[( MAX_ADDRESS + 7 ) / 8];
   44   // PNUM of the request
   45   uns8  PNUM;
   46   // PCMD of the request
   47   uns8  PCMD;
   48   // Optional PDATA
   49   uns8  PDATA[0];
   50 } TMultiRequest;
   51 
   52 // Request stored at bufferAUX. We can use bufferAUX because the Batch, that internally uses bufferAUX, is not used.
   53 TMultiRequest MultiRequest @ bufferAUX;
   54 
   55 #if &_DpaMessage.Request.PData[0] != &bufferRF[0] || &_DpaMessage.Response.PData[0] != &bufferRF[0]
   56 #error We assume DPA data @ bufferRF
   57 #endif
   58 
   59 // Indicate response will be sent back
   60 static bit sendResponse;
   61 
   62 void CancelResponse();
   63 
   64 #define   myHWPID 0xaAaF
   65 
   66 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
   67 //############################################################################################
   68 bit CustomDpaHandler()
   69 //############################################################################################
   70 {
   71   // Handler presence mark
   72   clrwdt();
   73 
   74   // Saved DPA request/response parameters
   75   static uns8 saveDpaDataLength, savePNUM, savePCMD;
   76   // Indicate multi-request was send
   77   static bit multiResponseRequested;
   78   // Delay to send the response back
   79   static uns24 responseDelay;
   80 
   81   // Detect DPA event to handle (unused event handlers can be commented out or even deleted)
   82   switch ( GetDpaEvent() )
   83   {
   84     // -------------------------------------------------
   85     case DpaEvent_Interrupt:
   86       // Do an extra quick background interrupt work
   87       // ! 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.
   88       // ! 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.
   89       // ! 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.
   90       // ! Only global variables or local ones marked by static keyword can be used to allow reentrancy.
   91       // ! Make sure race condition does not occur when accessing those variables at other places.
   92       // ! 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.
   93       // ! Do not call any OS functions except setINDFx().
   94       // ! Do not use any OS variables especially for writing access.
   95       // ! 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.
   96 
   97       //  If TMR6 interrupt occurred
   98       if ( TMR6IF )
   99       {
  100         // Unmask interrupt
  101         TMR6IF = 0;
  102         // Decrement tick count
  103 #ifndef __CC5XFREE__
  104         if ( responseDelay != 0 )
  105           responseDelay--;
  106 #else
  107         if ( ( responseDelay.low8 | responseDelay.mid8 | responseDelay.high8 ) != 0 )
  108         {
  109           W = 1;
  110           responseDelay.low8 -= W;
  111           W = 0;
  112           responseDelay.mid8 = subWFB( responseDelay.mid8 );
  113           responseDelay.high8 = subWFB( responseDelay.high8 );
  114         }
  115 #endif
  116         else
  117           TMR6ON = FALSE;
  118       }
  119 
  120       return Carry;
  121 
  122       // -------------------------------------------------
  123     case DpaEvent_Idle:
  124       // Do a quick background work when RF packet is not received
  125 
  126       // Ready to send the response?
  127       if ( sendResponse && !TMR6ON )
  128       {
  129         // Cancel response
  130         CancelResponse();
  131         // Number of hops = my VRN
  132         RTHOPS = ntwVRN;
  133         // Just generate new PID
  134         ++PID;
  135         // No DPA Params used
  136         _DpaParams = 0;
  137         // Send response to the coordinator
  138         _NADR = COORDINATOR_ADDRESS;
  139         // Recall stored parameters
  140         _PNUM = savePNUM;
  141         _PCMD = savePCMD;
  142         // Copy stored response data to the right place
  143         swapBufferINFO();
  144         copyBufferINFO2RF();
  145         // Prepare data length
  146         _DpaDataLength = saveDpaDataLength;
  147         // My HWPID
  148         _HWPID = myHWPID;
  149         // And finally send the response
  150         DpaApiRfTxDpaPacket( lastRSSI, 0xff );
  151       }
  152       break;
  153 
  154       // -------------------------------------------------
  155     case DpaEvent_Init:
  156       // Do a one time initialization before main loop starts
  157 
  158       // Setup TMR6 to generate ticks on the background (ticks every 10ms)
  159       PR6 = 250 - 1;
  160       // Prescaler 16, Postscaler 10, 16 * 10 * 250 = 40000 = 4MHz * 10ms
  161       T6CON = 0b0.1001.0.10;
  162       TMR6IE = TRUE;
  163       break;
  164 
  165       // -------------------------------------------------
  166     case DpaEvent_AfterSleep:
  167       // Called on wake-up from sleep
  168 
  169       // "Start" timer
  170       TMR6IE = TRUE;
  171       break;
  172 
  173       // -------------------------------------------------
  174     case DpaEvent_BeforeSleep:
  175       // Called before going to sleep   (the same handling as DpaEvent_DisableInterrupts event)
  176     case DpaEvent_DisableInterrupts:
  177       // Called when device needs all hardware interrupts to be disabled (before Reset, Restart, LoadCode, Remove bond, and Run RFPGM)
  178 
  179       // Must not use TMR6 any more
  180       TMR6ON = FALSE;
  181       TMR6IE = FALSE;
  182 
  183       // -------------------------------------------------
  184     case DpaEvent_FrcResponseTime:
  185       // Called to get FRC response time
  186     case DpaEvent_FrcValue:
  187       // Called to get FRC value
  188     case DpaEvent_ReceiveDpaRequest:
  189       // Called after DPA request was received
  190 
  191       // Events that will cancel sending response
  192       CancelResponse();
  193       break;
  194 
  195       // -------------------------------------------------
  196     case DpaEvent_AfterRouting:
  197       // Called after Notification and after routing of the DPA response was finished
  198 
  199       // Is there a multi-response?
  200       if ( multiResponseRequested )
  201       {
  202         // Reset the flag
  203         multiResponseRequested = FALSE;
  204         // Delay for the 1st VRN
  205         responseDelay = MIN_STD_TIMESLOT;
  206         // Bitmap bit mask
  207         uns8 mask = 0;
  208         // Bitmap byte pointer
  209 #if &MultiRequest.VrnBitmap[-1] != &bufferAUX[0]
  210         FSR0 = &MultiRequest.VrnBitmap[-1];
  211 #else
  212         setFSR0( _FSR_AUX );
  213 #endif
  214         // Checked VRN
  215         uns8 vrn;
  216         for ( vrn = 0; vrn <= ntwVRN; vrn++ )
  217         {
  218           // Next mask
  219           mask <<= 1;
  220           // Start with new mask and next byte?
  221           if ( mask == 0 )
  222           {
  223             // New mask
  224             mask.0 = 1;
  225             // Next byte
  226             FSR0++;
  227           }
  228 
  229           // Is the VRN addressed?
  230           if ( ( *FSR0 & mask ) != 0 )
  231           {
  232             // Is it me?
  233             if ( vrn == ntwVRN )
  234             {
  235               // I am addressed :-), so I will run the request and store a response for later sending
  236               // Prepare the request
  237               _PNUM = MultiRequest.PNUM;
  238               _PCMD = MultiRequest.PCMD;
  239               setFSR1( _FSR_RF );
  240               copyMemoryBlock( MultiRequest.PDATA, FSR1, _DpaDataLength = saveDpaDataLength - sizeof( MultiRequest ) );
  241               // Run the request. It should take the same time at all nodes.
  242               DpaApiLocalRequest();
  243               // Store the response parameters
  244               savePNUM = _PNUM;
  245               savePCMD = _PCMD;
  246               saveDpaDataLength = _DpaDataLength;
  247               // Store the response data to bufferAUX
  248               copyBufferRF2INFO();
  249               swapBufferINFO();
  250               // Delay to send the response is on
  251               sendResponse = TRUE;
  252               // Synchronize and start timer
  253               TMR6 = 0;
  254               TMR6ON = TRUE;
  255               break;
  256             }
  257             else
  258             {
  259               // Delay for the current VRN
  260               uns8 loop = MultiRequest.ResponseSlot;
  261               // Loop is faster and shorter than multiplication
  262               do
  263               {
  264 #ifndef __CC5XFREE__
  265                 responseDelay += vrn;
  266 #else
  267                 W = vrn;
  268                 responseDelay.low8 += W;
  269                 W = 0;
  270                 responseDelay.mid8 = addWFC( responseDelay.mid8 );
  271                 responseDelay.high8 = addWFC( responseDelay.high8 );
  272 #endif
  273               } while ( --loop != 0 );
  274               // Add some extra time for every addressed VRN before me
  275 #ifndef __CC5XFREE__
  276               responseDelay += MIN_STD_TIMESLOT;
  277 #else
  278               W = MIN_STD_TIMESLOT;
  279               responseDelay.low8 += W;
  280               W = 0;
  281               responseDelay.mid8 = addWFC( responseDelay.mid8 );
  282               responseDelay.high8 = addWFC( responseDelay.high8 );
  283 #endif
  284             }
  285           }
  286         }
  287         // Delay is computed, but I was not addressed :-(
  288       }
  289       break;
  290 
  291       // -------------------------------------------------
  292     case DpaEvent_DpaRequest:
  293       // Called to interpret DPA request for peripherals
  294 
  295       // Cancel sending multi response
  296       CancelResponse();
  297 
  298       // -------------------------------------------------
  299       // Peripheral enumeration
  300       if ( IsDpaEnumPeripheralsRequest() )
  301       {
  302         _DpaMessage.EnumPeripheralsAnswer.UserPerNr = 1;
  303         FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_USER + 0 );
  304         _DpaMessage.EnumPeripheralsAnswer.HWPID = myHWPID;
  305 
  306 DpaHandleReturnTRUE:
  307         return TRUE;
  308       }
  309       // -------------------------------------------------
  310       // Get information about peripheral
  311       else if ( IsDpaPeripheralInfoRequest() )
  312       {
  313         if ( _PNUM == PNUM_USER + 0 )
  314         {
  315           _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_USER_AREA;
  316           _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ_WRITE;
  317           goto DpaHandleReturnTRUE;
  318         }
  319         break;
  320       }
  321       // -------------------------------------------------
  322       else
  323       {
  324         // Handle peripheral command
  325         if ( _PNUM == PNUM_USER + 0 )
  326         {
  327           // Check the command value and other stuff
  328           if ( _PCMD == 0 &&
  329                // Data length is enough?
  330                _DpaDataLength >= sizeof( MultiRequest ) &&
  331                // Broadcast?
  332                _NADR == BROADCAST_ADDRESS &&
  333                // Am I routing?
  334                ( DpaApiReadConfigByte( CFGIND_DPA_FLAGS0 ) & 0b1000 ) == 0 &&
  335                // I do not have temporary address (i.e. no VRN)?
  336                ntwADDR != TEMPORARY_ADDRESS &&
  337                // I have a non-zero VRN (I am or I was discovered)
  338                ntwVRN != 0 &&
  339                // I am discovered with the last discovery?
  340                ntwDID == RTDID )
  341             // Then I can response back
  342           {
  343             // Save data length
  344             saveDpaDataLength = _DpaDataLength;
  345             // We will process the request after routing is over
  346             multiResponseRequested = TRUE;
  347             // Store TMultiRequest to the bufferAUX for lated processing
  348             copyBufferRF2INFO();
  349             swapBufferINFO();
  350             // Request was just OK (because of broadcast the response is not sent)
  351             goto DpaHandleReturnTRUE;
  352           }
  353 
  354           // Invalid command for some reason from above
  355           DpaApiReturnPeripheralError( ERROR_FAIL );
  356         }
  357       }
  358 
  359       break;
  360   }
  361 
  362   return FALSE;
  363 }
  364 //############################################################################################
  365 void CancelResponse()
  366 //############################################################################################
  367 {
  368   TMR6ON = FALSE;
  369   sendResponse = FALSE;
  370 }
  371 //############################################################################################
  372 // 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)
  373 #include "DPAcustomHandler.h"
  374 //############################################################################################