1 // **********************************************************************************
    2 //   Custom DPA Handler code example - Simple reflex game controlled by coordinator *
    3 // **********************************************************************************
    4 // Copyright (c) IQRF Tech s.r.o.
    5 //
    6 // File:    $RCSfile: CustomDpaHandler-Coordinator-ReflexGame.c,v $
    7 // Version: $Revision: 1.27 $
    8 // Date:    $Date: 2017/08/31 08:32:32 $
    9 //
   10 // Revision history:
   11 //   2017/03/13  Release for DPA 3.00
   12 //   2015/08/05  Release for DPA 2.20
   13 //   2014/10/31  Release for DPA 2.10
   14 //   2014/04/30  Release for DPA 2.00
   15 //
   16 // **********************************************************************************
   17 
   18 // Online DPA documentation http://www.iqrf.org/DpaTechGuide/
   19 
   20 // Default IQRF include (modify the path according to your setup)
   21 #include "IQRF.h"
   22 
   23 // Implement Custom DPA Handler for Coordinator
   24 #define COORDINATOR_CUSTOM_HANDLER
   25 
   26 // Default DPA header (modify the path according to your setup)
   27 #include "DPA.h"
   28 // Default Custom DPA Handler header (modify the path according to your setup)
   29 #include "DPAcustomHandler.h"
   30 
   31 // Application randomly chooses one of the gaming nodes to test the player's reaction
   32 // It tests whether the button (use e.g. at DK-EVAL-04) of the node is not pressed in advance
   33 // If so it tries to find another node with the button not being pressed
   34 // After a random time it pulses node's LEDR
   35 // If the player presses button within a certain time then LEDG at coordinator goes ON
   36 // If the player does not press the button then LEDR at coordinator goes ON
   37 // The game repeats again
   38 // This example works only at STD mode, not at LP mode
   39 
   40 // 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
   41 
   42 // Max. number of gaming nodes
   43 // The application uses nodes with VRN=1,2, ... ,NODES to play with
   44 #define NODES           4
   45 
   46 // Machine states
   47 typedef enum
   48 {
   49   // Send button off test
   50   state_TestOff,
   51   // Receive button off test
   52   state_TestOffResponse,
   53   // Pulse LED
   54   state_Pulse,
   55   // Send button on test
   56   state_Test,
   57   // Receive button on test
   58   state_TestResponse,
   59 } TState;
   60 
   61 // Must be the 1st defined function in the source code in order to be placed at the correct FLASH location!
   62 //############################################################################################
   63 bit CustomDpaHandler()
   64 //############################################################################################
   65 {
   66   // Handler presence mark
   67   clrwdt();
   68 
   69   // Addresses indexed by VRNs
   70   static uns8   nodes[NODES];
   71   // Number of VRNs found
   72   static uns8   nodesCnt;
   73   // Random value
   74   static uns8   rand;
   75   // Machine state
   76   static uns8   state;
   77   // Tested node VRN
   78   static uns8   nodeVrn;
   79   // Tested node address
   80   static uns8   nodeAddr;
   81 
   82   // Detect DPA event to handle
   83   switch ( GetDpaEvent() )
   84   {
   85     // -------------------------------------------------
   86     case DpaEvent_Init:
   87       // Do a one time initialization work before main loop starts
   88 
   89       // Find nodes with VRN starting 1 up to NODES, break if node not found
   90       for ( nodesCnt = 0; nodesCnt < NODES; nodesCnt++ )
   91       {
   92         // Try to find VRN among all nodes
   93         for ( RX = 0; RX <= MAX_ADDRESS; RX++ )
   94         {
   95           // Node is bonded and discovered
   96           if ( isBondedNode( RX ) && isDiscoveredNode( RX ) )
   97           {
   98             optimizeHops( 0xFF );
   99             // if VRN matches
  100             if ( RTHOPS - 1 == nodesCnt )
  101             {
  102               // Store its address
  103               writeToRAM( nodes + nodesCnt, RX );
  104               // and try to find next VRN
  105               goto nextVrn;
  106             }
  107           }
  108         }
  109 
  110         // Node with requested VRN not found
  111         break;
  112 
  113 nextVrn:
  114       }
  115 
  116       // Initialize random seed
  117       rand = lastRSSI ^ TMR1L;
  118       if ( rand == 0 )
  119         rand.0 = 1;
  120 
  121       // Set starting state
  122       state = state_TestOff;
  123 
  124       // Set starting delay 2s
  125       GIE = FALSE;
  126       DpaTicks = 100L * 2;
  127       GIE = TRUE;
  128 
  129       break;
  130 
  131       // -------------------------------------------------
  132     case DpaEvent_Idle:
  133       // Do a quick background work when RF packet is not received
  134 
  135       // Game runs only if interface master is not connected
  136       if ( IFaceMasterNotConnected )
  137       {
  138         // In the meantime generate new and new random values
  139         rand = lsr( rand );
  140         W = 0b10111000;
  141         if ( Carry )
  142           rand ^= W;
  143 
  144         // We run state machine within Idle event handler
  145         switch ( state )
  146         {
  147           case state_TestOff:
  148             // Timer is over?
  149             if ( DpaTicks.15 == 0 )
  150               break;
  151 
  152             // If no nodes found, then restart pulsing LEDR every 1s
  153             if ( nodesCnt == 0 )
  154             {
  155               pulsingLEDR();
  156               GIE = FALSE;
  157               DpaTicks = 1L * 100;
  158               GIE = TRUE;
  159               break;
  160             }
  161 
  162             // Generate random VRN to test
  163             nodeVrn = rand % nodesCnt;
  164             nodeVrn++;
  165             // Get its address
  166             nodeAddr = readFromRAM( nodes + nodeVrn - 1 );
  167             // fall through!
  168 
  169           case state_Test:
  170             // Wait for the testing DPA request
  171             if ( DpaTicks.15 == 0 )
  172               break;
  173 
  174             // DPA request to test the button pin
  175             _NADR = nodeAddr;
  176             _NADRhigh = 0;
  177             // Use IO peripheral
  178             _PNUM = PNUM_IO;
  179             // Read GPIOs
  180             _PCMD = CMD_IO_GET;
  181             // Any HWPID
  182             _HWPID = HWPID_DoNotCheck;
  183             // This DPA request has no data
  184             _DpaDataLength = 0;
  185             // Send the DPA request
  186             DpaApiRfTxDpaPacketCoordinator();
  187             // Setup safe 1s timeout to recover from not receiving the DPA response at state state_Response
  188             GIE = FALSE;
  189             DpaTicks = 1 * 100L;
  190             GIE = TRUE;
  191             // Next state
  192             state++;
  193             break;
  194 
  195           case state_Pulse:
  196             // DPA request to pulse LEDR in the tested node by batch (this is done synchronously in the precise time)
  197             _NADR = nodeAddr;
  198             _NADRhigh = 0;
  199             // Use IO peripheral to work with LEDs even if LED peripherals are not present
  200             _PNUM = PNUM_IO;
  201             // Make a LEDR pulse
  202             _PCMD = CMD_IO_SET;
  203             // Any HWPID
  204             _HWPID = HWPID_DoNotCheck;
  205 
  206             // LEDR=1 => Set PORTA.2 to 1
  207             _DpaMessage.PerIoDirectionAndSet_Request.Triplets[0].Port = PNUM_IO_PORTA; // PORTA
  208             _DpaMessage.PerIoDirectionAndSet_Request.Triplets[0].Mask = 0b0000.0100;    // bit 2
  209             _DpaMessage.PerIoDirectionAndSet_Request.Triplets[0].Value = 0b0000.0100;   // bit 2 = 1
  210 
  211             // 64 ms pulse
  212             _DpaMessage.PerIoDirectionAndSet_Request.Delays[1].Header = PNUM_IO_DELAY;  // delay
  213             _DpaMessage.PerIoDirectionAndSet_Request.Delays[1].Delay = 64;           // 64
  214 
  215             // LEDR=0 => Set PORTA.2 to 0
  216             _DpaMessage.PerIoDirectionAndSet_Request.Triplets[2].Port = PNUM_IO_PORTA; // PORTA
  217             _DpaMessage.PerIoDirectionAndSet_Request.Triplets[2].Mask = 0b0000.0100;    // bit 2
  218             _DpaMessage.PerIoDirectionAndSet_Request.Triplets[2].Value = 0b0000.0000;   // bit 2 = 0
  219 
  220             // Setup the correct length of data
  221             _DpaDataLength = 3 * sizeof( _DpaMessage.PerIoDirectionAndSet_Request.Triplets[0] );
  222 
  223             // Send the DPA request
  224             DpaApiRfTxDpaPacketCoordinator();
  225 
  226             // Set timeout in the way that the testing of the button will be at the same time as possible independently on the node VRN
  227             GIE = FALSE;
  228             DpaTicks = 35L + nodeVrn * MIN_TIMESLOT - 2 * MIN_TIMESLOT;
  229             GIE = TRUE;
  230             // Next state
  231             state++;
  232             break;
  233 
  234           case state_TestOffResponse:
  235           case state_TestResponse:
  236             // Did we get DPA response within timeout?
  237             if ( DpaTicks.15 )
  238               // No, go to the 1st state
  239               state = state_TestOff;
  240 
  241             break;
  242         }
  243       }
  244 
  245       break;
  246 
  247       // -------------------------------------------------
  248     case DpaEvent_ReceiveDpaResponse:
  249       // Called after DPA response was received at coordinator
  250 
  251       // Did we get the testing response?
  252       if ( _NADR == nodeAddr && _PNUM == PNUM_IO && _PCMD == ( CMD_IO_GET | RESPONSE_FLAG ) )
  253       {
  254         if ( state == state_TestResponse )
  255         {
  256           // Long pulse
  257           setOnPulsingLED( 30 );
  258           //  and the color depends on the state of the button PIN @ PORTA.5
  259           if ( _DpaMessage.Response.PData[0].5 ) 
  260             pulseLEDR();
  261           else
  262             pulseLEDG();
  263 
  264 startMachine:
  265           // Set the starting state
  266           state = state_TestOff;
  267           // With random time to start new test
  268           GIE = FALSE;
  269           DpaTicks = ( 2 * 100L ) + rand / 2;
  270           GIE = TRUE;
  271 
  272           // By returning true consume the DPA request
  273 DpaHandleReturnTRUE:
  274           return TRUE;
  275         }
  276 
  277         if ( state == state_TestOffResponse )
  278         {
  279           // Button @ PORTA.5 must be off
  280           if ( !_DpaMessage.Response.PData[0].5 )
  281             goto startMachine;
  282 
  283           state = state_Pulse;
  284 
  285           // By returning true consume the DPA request
  286           goto DpaHandleReturnTRUE;
  287         }
  288       }
  289 
  290       break;
  291   }
  292 
  293   return FALSE;
  294 }
  295 
  296 //############################################################################################
  297 // 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) 
  298 #include "DPAcustomHandler.h"
  299 //############################################################################################