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.15 $
    8 // Date:    $Date: 2017/08/15 19:49:10 $
    9 //
   10 // Revision history:
   11 //   2017/03/13  Release for DPA 3.00
   12 //
   13 // *********************************************************************
   14 
   15 // Online DPA documentation http://www.iqrf.org/DpaTechGuide/
   16 
   17 // This is an !EXPERIMENTAL EXAMPLE! that shows how to receive responses from more nodes to the one request.
   18 // This example works only at STD mode.
   19 // Because of used uns24 type it requires an extended version of CC5X compiler.
   20 
   21 // 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.
   22 // Please see TRequest structure below. Just broadcast this structure at PDATA top the PNUM=0x20 and PCMD=0x00.
   23 // Only addressed, discovered and routing nodes will respond.
   24 
   25 // Default IQRF include (modify the path according to your setup)
   26 #include "IQRF.h"
   27 // Default DPA header (modify the path according to your setup)
   28 #include "DPA.h"
   29 // Default Custom DPA Handler header (modify the path according to your setup)
   30 #include "DPAcustomHandler.h"
   31 
   32 // The example will not work at demo DPA version because demo DPA does not support the following Custom DPA Handler events: DpaEvent_Interrupt, DpaEvent_BeforeSleep, DpaEvent_AfterSleep, DpaEvent_Reset, DpaEvent_DisableInterrupts, DpaEvent_ReceiveDpaResponse, DpaEvent_IFaceReceive, DpaEvent_ReceiveDpaRequest, DpaEvent_BeforeSendingDpaResponse
   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 response
   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         if ( responseDelay != 0 )
  104           responseDelay--;
  105         else
  106           TMR6ON = FALSE;
  107       }
  108 
  109       return Carry;
  110 
  111       // -------------------------------------------------
  112     case DpaEvent_Idle:
  113       // Do a quick background work when RF packet is not received
  114 
  115       // Ready to send the response?
  116       if ( sendResponse && !TMR6ON )
  117       {
  118         // Cancel response
  119         CancelResponse();
  120         // Number of hops = my VRN
  121         RTHOPS = ntwVRN;
  122         // Just generate new PID
  123         ++PID;
  124         // No DPA Params used
  125         _DpaParams = 0;
  126         // Send response to the coordinator
  127         _NADR = COORDINATOR_ADDRESS;
  128         // Recall stored parameters
  129         _PNUM = savePNUM;
  130         _PCMD = savePCMD;
  131         // Copy stored response data to the right place 
  132         swapBufferINFO();
  133         copyBufferINFO2RF();
  134         // Prepare data length
  135         _DpaDataLength = saveDpaDataLength;
  136         // My HWPID
  137         _HWPID = myHWPID;
  138         // And finally send the response
  139         DpaApiRfTxDpaPacket( lastRSSI, 0xff );
  140       }
  141       break;
  142 
  143       // -------------------------------------------------
  144     case DpaEvent_Init:
  145       // Do a one time initialization work before main loop starts
  146 
  147       // Setup TMR6 to generate ticks on the background (ticks every 10ms)
  148       PR6 = 250 - 1;
  149       // Prescaler 16, Postscaler 10, 16 * 10 * 250 = 40000 = 4MHz * 10ms
  150       T6CON = 0b0.1001.0.10;
  151       TMR6IE = TRUE;
  152       break;
  153 
  154       // -------------------------------------------------
  155     case DpaEvent_AfterSleep:
  156       // Called on wake-up from sleep
  157 
  158       // "Start" timer
  159       TMR6IE = TRUE;
  160       break;
  161 
  162       // -------------------------------------------------
  163     case DpaEvent_BeforeSleep:
  164       // Called before going to sleep   (the same handling as DpaEvent_DisableInterrupts event)
  165     case DpaEvent_DisableInterrupts:
  166       // Called when device needs all hardware interrupts to be disabled (before Reset, Restart and RFPGM)
  167 
  168       // Must not use TMR6 any more
  169       TMR6ON = FALSE;
  170       TMR6IE = FALSE;
  171 
  172       // -------------------------------------------------
  173     case DpaEvent_FrcResponseTime:
  174       // Called to get FRC response time
  175     case DpaEvent_FrcValue:
  176       // Called to get FRC value
  177     case DpaEvent_ReceiveDpaRequest:
  178       // Called after DPA request was received
  179 
  180       // Events that will cancel sending response
  181       CancelResponse();
  182       break;
  183 
  184       // -------------------------------------------------
  185     case DpaEvent_AfterRouting:
  186       // Called after Notification and after routing of the DPA response was finished
  187 
  188       // Is there a multi-response?
  189       if ( multiResponseRequested )
  190       {
  191         // Reset the flag
  192         multiResponseRequested = FALSE;
  193         // Delay for the 1st VRN
  194         responseDelay = MIN_TIMESLOT;
  195         // Bitmap bit mask
  196         uns8 mask = 0;
  197         // Bitmap byte pointer
  198 #if &MultiRequest.VrnBitmap[-1] != &bufferAUX[0]
  199         FSR0 = &MultiRequest.VrnBitmap[-1];
  200 #else
  201         setFSR0( _FSR_AUX );
  202 #endif
  203         // Checked VRN
  204         uns8 vrn;
  205         for ( vrn = 0; vrn <= ntwVRN; vrn++ )
  206         {
  207           // Next mask
  208           mask <<= 1;
  209           // Start with new mask and next byte?
  210           if ( mask == 0 )
  211           {
  212             // New mask
  213             mask.0 = 1;
  214             // Next byte
  215             FSR0++;
  216           }
  217 
  218           // Is the VRN addressed?
  219           if ( ( *FSR0 & mask ) != 0 )
  220           {
  221             // Is it me?
  222             if ( vrn == ntwVRN )
  223             {
  224               // I am addressed :-), so I will run the request and store a response for later sending
  225               // Prepare the request
  226               _PNUM = MultiRequest.PNUM;
  227               _PCMD = MultiRequest.PCMD;
  228               setFSR1( _FSR_RF );
  229               copyMemoryBlock( MultiRequest.PDATA, FSR1, _DpaDataLength = saveDpaDataLength - sizeof( MultiRequest ) );
  230               // Run the request. It should take the same time at all nodes.
  231               DpaApiLocalRequest();
  232               // Store the response parameters
  233               savePNUM = _PNUM;
  234               savePCMD = _PCMD;
  235               saveDpaDataLength = _DpaDataLength;
  236               // Store the response data to bufferAUX
  237               copyBufferRF2INFO();
  238               swapBufferINFO();
  239               // Delay to send the response is on
  240               sendResponse = TRUE;
  241               // Synchronize and start timer
  242               TMR6 = 0;
  243               TMR6ON = TRUE;
  244               break;
  245             }
  246             else
  247             {
  248               // Delay for the current VRN
  249               uns8 loop = MultiRequest.ResponseSlot;
  250               // Loop is faster and shorter than multiplication
  251               do
  252               {
  253                 responseDelay += vrn;
  254               } while ( --loop != 0 );
  255               // Add some extra time for every addressed VRN before me
  256               responseDelay += MIN_TIMESLOT;
  257             }
  258           }
  259         }
  260         // Delay is computed, but I was not addressed :-(
  261       }
  262       break;
  263 
  264       // -------------------------------------------------
  265     case DpaEvent_DpaRequest:
  266       // Called to interpret DPA request for peripherals
  267 
  268       // Cancel sending multi response
  269       CancelResponse();
  270 
  271       // -------------------------------------------------
  272       // Peripheral enumeration
  273       if ( IsDpaEnumPeripheralsRequest() )
  274       {
  275         _DpaMessage.EnumPeripheralsAnswer.UserPerNr = 1;
  276         FlagUserPer( _DpaMessage.EnumPeripheralsAnswer.UserPer, PNUM_USER + 0 );
  277         _DpaMessage.EnumPeripheralsAnswer.HWPID = myHWPID;
  278 
  279 DpaHandleReturnTRUE:
  280         return TRUE;
  281       }
  282       // -------------------------------------------------
  283       // Get information about peripheral
  284       else if ( IsDpaPeripheralInfoRequest() )
  285       {
  286         if ( _PNUM == PNUM_USER + 0 )
  287         {
  288           _DpaMessage.PeripheralInfoAnswer.PerT = PERIPHERAL_TYPE_USER_AREA;
  289           _DpaMessage.PeripheralInfoAnswer.PerTE = PERIPHERAL_TYPE_EXTENDED_READ_WRITE;
  290           goto DpaHandleReturnTRUE;
  291         }
  292         break;
  293       }
  294       // -------------------------------------------------
  295       else
  296       {
  297         // Handle peripheral command
  298         if ( _PNUM == PNUM_USER + 0 )
  299         {
  300           // Check the command value and other stuff
  301           if ( _PCMD == 0 &&
  302                // Data length is enough?
  303                _DpaDataLength >= sizeof( MultiRequest ) &&
  304                // Broadcast?
  305                _NADR == BROADCAST_ADDRESS &&
  306                // Am I routing?
  307                ( DpaApiReadConfigByte( CFGIND_DPA_FLAGS ) & 0b1000 ) == 0 &&
  308                // I do not have temporary address (i.e. no VRN)?
  309                ntwADDR != TEMPORARY_ADDRESS &&
  310                // I have a non-zero VRN (I am or I was discovered)
  311                ntwVRN != 0 &&
  312                // I am discovered with the last discovery?
  313                ntwDID == RTDID )
  314             // Then I can response back
  315           {
  316             // Save data length
  317             saveDpaDataLength = _DpaDataLength;
  318             // We will process the request after routing is over
  319             multiResponseRequested = TRUE;
  320             // Store TMultiRequest to the bufferAUX for lated processing
  321             copyBufferRF2INFO();
  322             swapBufferINFO();
  323             // Request was just OK (because of broadcast the response is not sent)
  324             goto DpaHandleReturnTRUE;
  325           }
  326 
  327           // Invalid command for some reason from above
  328           DpaApiReturnPeripheralError( ERROR_FAIL );
  329         }
  330       }
  331 
  332       break;
  333   }
  334 
  335   return FALSE;
  336 }
  337 //############################################################################################
  338 void CancelResponse()
  339 //############################################################################################
  340 {
  341   TMR6ON = FALSE;
  342   sendResponse = FALSE;
  343 }
  344 //############################################################################################
  345 // 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) 
  346 #include "DPAcustomHandler.h"
  347 //############################################################################################