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.36 $
    8 // Date:    $Date: 2019/01/10 09:57:23 $
    9 //
   10 // Revision history:
   11 //   2019/01/10  Release for DPA 4.00
   12 //   2018/10/25  Release for DPA 3.03
   13 //   2017/03/13  Release for DPA 3.00
   14 //   2015/08/05  Release for DPA 2.20
   15 //   2014/10/31  Release for DPA 2.10
   16 //   2014/04/30  Release for DPA 2.00
   17 //
   18 // **********************************************************************************
   19 
   20 // Online DPA documentation http://www.iqrf.org/DpaTechGuide/
   21 
   22 // Default IQRF include (modify the path according to your setup)
   23 #include "IQRF.h"
   24 
   25 // Implement Custom DPA Handler for Coordinator
   26 #define COORDINATOR_CUSTOM_HANDLER
   27 
   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 // Application randomly chooses one of the gaming nodes to test the player's reaction
   34 // It tests whether the button (use e.g. at DK-EVAL-04) of the node is not pressed in advance
   35 // If so it tries to find another node with the button not being pressed
   36 // After a random time it pulses node's LEDR
   37 // If the player presses button within a certain time then LEDG at coordinator goes ON
   38 // If the player does not press the button then LEDR at coordinator goes ON
   39 // The game repeats again
   40 // This example works only at STD mode, not at LP mode
   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             FSR0 = &nodes[nodeVrn - 1];
  167             nodeAddr = *FSR0;
  168             // fall through!
  169 
  170           case state_Test:
  171             // Wait for the testing DPA request
  172             if ( DpaTicks.15 == 0 )
  173               break;
  174 
  175             // DPA request to test the button pin
  176             _NADR = nodeAddr;
  177             _NADRhigh = 0;
  178             // Use IO peripheral
  179             _PNUM = PNUM_IO;
  180             // Read GPIOs
  181             _PCMD = CMD_IO_GET;
  182             // Any HWPID
  183             _HWPID = HWPID_DoNotCheck;
  184             // This DPA request has no data
  185             _DpaDataLength = 0;
  186             // Send the DPA request
  187             DpaApiRfTxDpaPacketCoordinator();
  188             // Setup safe 1s timeout to recover from not receiving the DPA response at state state_Response
  189             GIE = FALSE;
  190             DpaTicks = 1 * 100L;
  191             GIE = TRUE;
  192             // Next state
  193             state++;
  194             break;
  195 
  196           case state_Pulse:
  197             // DPA request to pulse LEDR in the tested node by batch (this is done synchronously in the precise time)
  198             _NADR = nodeAddr;
  199             _NADRhigh = 0;
  200             // Use IO peripheral to work with LEDs even if LED peripherals are not present
  201             _PNUM = PNUM_IO;
  202             // Make a LEDR pulse
  203             _PCMD = CMD_IO_SET;
  204             // Any HWPID
  205             _HWPID = HWPID_DoNotCheck;
  206 
  207             // LEDR=1 => Set PORTA.2 to 1
  208             _DpaMessage.PerIoDirectionAndSet_Request.Triplets[0].Port = PNUM_IO_PORTA; // PORTA
  209             _DpaMessage.PerIoDirectionAndSet_Request.Triplets[0].Mask = 0b0000.0100;    // bit 2
  210             _DpaMessage.PerIoDirectionAndSet_Request.Triplets[0].Value = 0b0000.0100;   // bit 2 = 1
  211 
  212             // 64 ms pulse
  213             _DpaMessage.PerIoDirectionAndSet_Request.Delays[1].Header = PNUM_IO_DELAY;  // delay
  214             _DpaMessage.PerIoDirectionAndSet_Request.Delays[1].Delay = 64;           // 64
  215 
  216             // LEDR=0 => Set PORTA.2 to 0
  217             _DpaMessage.PerIoDirectionAndSet_Request.Triplets[2].Port = PNUM_IO_PORTA; // PORTA
  218             _DpaMessage.PerIoDirectionAndSet_Request.Triplets[2].Mask = 0b0000.0100;    // bit 2
  219             _DpaMessage.PerIoDirectionAndSet_Request.Triplets[2].Value = 0b0000.0000;   // bit 2 = 0
  220 
  221             // Setup the correct length of data
  222             _DpaDataLength = 3 * sizeof( _DpaMessage.PerIoDirectionAndSet_Request.Triplets[0] );
  223 
  224             // Send the DPA request
  225             DpaApiRfTxDpaPacketCoordinator();
  226 
  227             // Set timeout in the way that the testing of the button will be at the same time as possible independently on the node VRN
  228             GIE = FALSE;
  229             DpaTicks = 35L + nodeVrn * MIN_STD_TIMESLOT - 2 * MIN_STD_TIMESLOT;
  230             GIE = TRUE;
  231             // Next state
  232             state++;
  233             break;
  234 
  235           case state_TestOffResponse:
  236           case state_TestResponse:
  237             // Did we get DPA response within timeout?
  238             if ( DpaTicks.15 )
  239               // No, go to the 1st state
  240               state = state_TestOff;
  241 
  242             break;
  243         }
  244       }
  245 
  246       break;
  247 
  248       // -------------------------------------------------
  249     case DpaEvent_ReceiveDpaResponse:
  250       // Called after DPA response was received at coordinator
  251 
  252       // Did we get the testing response?
  253       if ( _NADR == nodeAddr && _PNUM == PNUM_IO && _PCMD == ( CMD_IO_GET | RESPONSE_FLAG ) )
  254       {
  255         if ( state == state_TestResponse )
  256         {
  257           // Long pulse
  258           setOnPulsingLED( 30 );
  259           //  and the color depends on the state of the button PIN @ PORTA.5
  260           if ( _DpaMessage.Response.PData[0].5 )
  261             pulseLEDR();
  262           else
  263             pulseLEDG();
  264 
  265 startMachine:
  266           // Set the starting state
  267           state = state_TestOff;
  268           // With random time to start new test
  269           GIE = FALSE;
  270           DpaTicks = ( 2 * 100L ) + rand / 2;
  271           GIE = TRUE;
  272 
  273           // By returning true consume the DPA request
  274 DpaHandleReturnTRUE:
  275           return TRUE;
  276         }
  277 
  278         if ( state == state_TestOffResponse )
  279         {
  280           // Button @ PORTA.5 must be off
  281           if ( !_DpaMessage.Response.PData[0].5 )
  282             goto startMachine;
  283 
  284           state = state_Pulse;
  285 
  286           // By returning true consume the DPA request
  287           goto DpaHandleReturnTRUE;
  288         }
  289       }
  290 
  291       break;
  292   }
  293 
  294   return FALSE;
  295 }
  296 
  297 //############################################################################################
  298 // 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) 
  299 #include "DPAcustomHandler.h"
  300 //############################################################################################