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