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